/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

/*
=======================================================================

USER INTERFACE MAIN

=======================================================================
*/

// use this to get a demo build without an explicit demo build, i.e. to get the demo ui files to build
//#define PRE_RELEASE_TADEMO

#include "ui_local.h"

uiInfo_t        uiInfo;

static const char *MonthAbbrev[] = {
	"Jan", "Feb", "Mar",
	"Apr", "May", "Jun",
	"Jul", "Aug", "Sep",
	"Oct", "Nov", "Dec"
};


static const char *skillLevels[] = {
	"I Can Win",
	"Bring It On",
	"Hurt Me Plenty",
	"Hardcore",
	"Nightmare"
};

static const int numSkillLevels = sizeof(skillLevels) / sizeof(const char *);


static const char *netSources[] = {
	"Local",
	"Mplayer",
	"Internet",
	"Favorites"
};
static const int numNetSources = sizeof(netSources) / sizeof(const char *);

static const serverFilter_t serverFilters[] = {
	{"All", ""},
	{"Quake 3 Arena", ""},
	{"Team Arena", "missionpack"},
	{"Rocket Arena", "arena"},
	{"Alliance", "alliance20"},
	{"Weapons Factory Arena", "wfa"},
	{"OSP", "osp"},
};

static const char *teamArenaGameTypes[] = {
	"FFA",
	"TOURNAMENT",
	"SP",
	"TEAM DM",
	"CTF",
	"1FCTF",
	"OVERLOAD",
	"HARVESTER",
	"TEAMTOURNAMENT"
};

static int const numTeamArenaGameTypes = sizeof(teamArenaGameTypes) / sizeof(const char *);


static const char *teamArenaGameNames[] = {
	"Free For All",
	"Tournament",
	"Single Player",
	"Team Deathmatch",
	"Capture the Flag",
	"One Flag CTF",
	"Overload",
	"Harvester",
	"Team Tournament",
};

static int const numTeamArenaGameNames = sizeof(teamArenaGameNames) / sizeof(const char *);


static const int numServerFilters = sizeof(serverFilters) / sizeof(serverFilter_t);

static const char *sortKeys[] = {
	"Server Name",
	"Map Name",
	"Open Player Spots",
	"Game Type",
	"Ping Time"
};
static const int numSortKeys = sizeof(sortKeys) / sizeof(const char *);

static char    *netnames[] = {
	"???",
	"UDP",
	"IPX",
	NULL
};

static const creditRow_t credits[] = {
	{"Dretchstorm Team","","",""},
	{"","","",""},
	{"ghostshell",   "USA",    "Code",  "Game programming, UI design and project direction"},
	{"evilchampion", "Canada", "Code",  "Lua scripts, game programming, IRC"},
	{"sumburn",      "USA",    "Design", "Hi-res textures and meshes, logo"},
	{"Brandon",      "USA",    "Sound",  "Music and sound effects"},
	{"","","",""},
	{"Dretchstorm Contributors","","",""},
	{"","","",""},
	{"Bad_Command",  "Canada", "Code",   "Game programming"},
	{"","","",""}
};

static const int numCredits = sizeof(credits) / sizeof(creditRow_t);


static int      gamecodetoui[] = { 4, 2, 3, 0, 5, 1, 6 };


static void     UI_StartServerRefresh(qboolean full);
static void     UI_StopServerRefresh(void);
static void     UI_DoServerRefresh(void);
static void     UI_FeederSelection(float feederID, int index);
static void     UI_BuildServerDisplayList(qboolean force);
static void     UI_BuildServerStatus(qboolean force);
static void     UI_BuildFindPlayerList(qboolean force);
static int QDECL UI_ServersQsortCompare(const void *arg1, const void *arg2);
static int      UI_MapCountByGameType(qboolean singlePlayer);
static int      UI_HeadCountByTeam(void);
static const char *UI_SelectedMap(int index, int *actual);
static const char *UI_SelectedHead(int index, int *actual);
static int      UI_GetIndexFromSelection(int actual);

int             ProcessNewUI(int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6);

/*
================
vmMain

This is the only way control passes into the module.
This must be the very first function compiled into the .qvm file
================
*/
vmCvar_t        ui_new;
vmCvar_t        ui_debug;
vmCvar_t        ui_initialized;
vmCvar_t        ui_teamArenaFirstRun;

void            _UI_Init(qboolean);
void            _UI_Shutdown(void);
void            _UI_KeyEvent(int key, qboolean down);
void            _UI_MouseEvent(int dx, int dy);
void            _UI_Refresh(int realtime);
qboolean        _UI_IsFullscreen(void);
intptr_t vmMain(int command, int arg0, int arg1, int arg2, int arg3,
				int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11)
{
	switch (command)
	{
		case UI_GETAPIVERSION:
			return UI_API_VERSION;

		case UI_INIT:
			_UI_Init(arg0);
			return 0;

		case UI_SHUTDOWN:
			_UI_Shutdown();
			return 0;

		case UI_KEY_EVENT:
			_UI_KeyEvent(arg0, arg1);
			return 0;

		case UI_MOUSE_EVENT:
			_UI_MouseEvent(arg0, arg1);
			return 0;

		case UI_REFRESH:
			_UI_Refresh(arg0);
			return 0;

		case UI_IS_FULLSCREEN:
			return _UI_IsFullscreen();

		case UI_SET_ACTIVE_MENU:
			_UI_SetActiveMenu(arg0);
			return 0;

		case UI_CONSOLE_COMMAND:
			return UI_ConsoleCommand(arg0);

		case UI_DRAW_CONNECT_SCREEN:
			UI_DrawConnectScreen(arg0);
			return 0;
	}

	return -1;
}



void AssetCache(void)
{
	uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(ASSET_GRADIENTBAR);
	uiInfo.uiDC.Assets.scrollBar = trap_R_RegisterShaderNoMip(ASSET_SCROLLBAR);
	uiInfo.uiDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip(ASSET_SCROLLBAR_ARROWDOWN);
	uiInfo.uiDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip(ASSET_SCROLLBAR_ARROWUP);
	uiInfo.uiDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip(ASSET_SCROLLBAR_ARROWLEFT);
	uiInfo.uiDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip(ASSET_SCROLLBAR_ARROWRIGHT);
	uiInfo.uiDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip(ASSET_SCROLL_THUMB);
	uiInfo.uiDC.Assets.sliderBar = trap_R_RegisterShaderNoMip(ASSET_SLIDER_BAR);
	uiInfo.uiDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip(ASSET_SLIDER_THUMB);
}

void _UI_DrawSides(float x, float y, float w, float h, float size)
{
	UI_AdjustFrom640(&x, &y, &w, &h);
	size *= uiInfo.uiDC.xscale;
	trap_R_DrawStretchPic(x, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader);
	trap_R_DrawStretchPic(x + w - size, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader);
}

void _UI_DrawTopBottom(float x, float y, float w, float h, float size)
{
	UI_AdjustFrom640(&x, &y, &w, &h);
	size *= uiInfo.uiDC.yscale;
	trap_R_DrawStretchPic(x, y, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader);
	trap_R_DrawStretchPic(x, y + h - size, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader);
}

/*
================
UI_DrawRect

Coordinates are 640*480 virtual values
=================
*/
void _UI_DrawRect(float x, float y, float width, float height, float size, const float *color)
{
	trap_R_SetColor(color);

	_UI_DrawTopBottom(x, y, width, height, size);
	_UI_DrawSides(x, y, width, height, size);

	trap_R_SetColor(NULL);
}




int Text_Width(const char *text, float scale, int limit)
{
	int             count, len;
	float           out;
	glyphInfo_t    *glyph;
	float           useScale;
	const char     *s = text;
	fontInfo_t     *font = &uiInfo.uiDC.Assets.textFont;

	if(scale <= ui_smallFont.value)
	{
		font = &uiInfo.uiDC.Assets.smallFont;
	}
	else if(scale >= ui_bigFont.value)
	{
		font = &uiInfo.uiDC.Assets.bigFont;
	}
	useScale = scale * font->glyphScale;
	out = 0;
	if(text)
	{
		len = strlen(text);
		if(limit > 0 && len > limit)
		{
			len = limit;
		}
		count = 0;
		while(s && *s && count < len)
		{
			if(Q_IsColorString(s))
			{
				s += 2;
				continue;
			}
			else
			{
				glyph = &font->glyphs[(int)*s];
				out += glyph->xSkip;
				s++;
				count++;
			}
		}
	}
	return out * useScale;
}

int Text_Height(const char *text, float scale, int limit)
{
	int             len, count;
	float           max;
	glyphInfo_t    *glyph;
	float           useScale;
	const char     *s = text;	// bk001206 - unsigned
	fontInfo_t     *font = &uiInfo.uiDC.Assets.textFont;

	if(scale <= ui_smallFont.value)
	{
		font = &uiInfo.uiDC.Assets.smallFont;
	}
	else if(scale >= ui_bigFont.value)
	{
		font = &uiInfo.uiDC.Assets.bigFont;
	}
	useScale = scale * font->glyphScale;
	max = 0;
	if(text)
	{
		len = strlen(text);
		if(limit > 0 && len > limit)
		{
			len = limit;
		}
		count = 0;
		while(s && *s && count < len)
		{
			if(Q_IsColorString(s))
			{
				s += 2;
				continue;
			}
			else
			{
				glyph = &font->glyphs[(int)*s];
				if(max < glyph->height)
				{
					max = glyph->height;
				}
				s++;
				count++;
			}
		}
	}
	return max * useScale;
}

void Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2,
					qhandle_t hShader)
{
	float           w, h;

	w = width * scale;
	h = height * scale;
	UI_AdjustFrom640(&x, &y, &w, &h);
	trap_R_DrawStretchPic(x, y, w, h, s, t, s2, t2, hShader);
}

void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style)
{
	int             len, count;
	vec4_t          newColor;
	glyphInfo_t    *glyph;
	float           useScale;
	fontInfo_t     *font = &uiInfo.uiDC.Assets.textFont;

	if(scale <= ui_smallFont.value)
	{
		font = &uiInfo.uiDC.Assets.smallFont;
	}
	else if(scale >= ui_bigFont.value)
	{
		font = &uiInfo.uiDC.Assets.bigFont;
	}
	useScale = scale * font->glyphScale;
	if(text)
	{
		const char     *s = text;	// bk001206 - unsigned

		trap_R_SetColor(color);
		memcpy(&newColor[0], &color[0], sizeof(vec4_t));
		len = strlen(text);
		if(limit > 0 && len > limit)
		{
			len = limit;
		}
		count = 0;
		while(s && *s && count < len)
		{
			glyph = &font->glyphs[(int)*s];
			//int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top;
			//float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height);
			if(Q_IsColorString(s))
			{
				memcpy(newColor, g_color_table[ColorIndex(*(s + 1))], sizeof(newColor));
				newColor[3] = color[3];
				trap_R_SetColor(newColor);
				s += 2;
				continue;
			}
			else
			{
				float           yadj = useScale * glyph->top;

				if(style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE)
				{
					int             ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;

					colorBlack[3] = newColor[3];
					trap_R_SetColor(colorBlack);
					Text_PaintChar(x + ofs, y - yadj + ofs,
								   glyph->imageWidth,
								   glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);
					trap_R_SetColor(newColor);
					colorBlack[3] = 1.0;
				}
				else if(style == ITEM_TEXTSTYLE_NEON)
				{
					vec4_t          glow, outer, inner, white;

					glow[0] = newColor[0] * 0.5;
					glow[1] = newColor[1] * 0.5;
					glow[2] = newColor[2] * 0.5;
					glow[3] = newColor[3] * 0.2;

					outer[0] = newColor[0];
					outer[1] = newColor[1];
					outer[2] = newColor[2];
					outer[3] = newColor[3];

					inner[0] = newColor[0] * 1.5 > 1.0f ? 1.0f : newColor[0] * 1.5;
					inner[1] = newColor[1] * 1.5 > 1.0f ? 1.0f : newColor[1] * 1.5;
					inner[2] = newColor[2] * 1.5 > 1.0f ? 1.0f : newColor[2] * 1.5;
					inner[3] = newColor[3];

					white[0] = white[1] = white[2] = white[3] = 1.0f;

					trap_R_SetColor(glow);
					Text_PaintChar(x - 1.5, y - yadj - 1.5,
								   glyph->imageWidth + 3,
								   glyph->imageHeight + 3, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

					trap_R_SetColor(outer);
					Text_PaintChar(x - 1, y - yadj - 1,
								   glyph->imageWidth + 2,
								   glyph->imageHeight + 2, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

					trap_R_SetColor(inner);
					Text_PaintChar(x - 0.5, y - yadj - 0.5,
								   glyph->imageWidth + 1,
								   glyph->imageHeight + 1, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

					trap_R_SetColor(white);
				}

				Text_PaintChar(x, y - yadj,
							   glyph->imageWidth,
							   glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

				x += (glyph->xSkip * useScale) + adjust;
				s++;
				count++;
			}
		}
		trap_R_SetColor(NULL);
	}
}

void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit,
						  int style)
{
	int             len, count;
	vec4_t          newColor;
	glyphInfo_t    *glyph, *glyph2;
	float           yadj;
	float           useScale;
	fontInfo_t     *font = &uiInfo.uiDC.Assets.textFont;

	if(scale <= ui_smallFont.value)
	{
		font = &uiInfo.uiDC.Assets.smallFont;
	}
	else if(scale >= ui_bigFont.value)
	{
		font = &uiInfo.uiDC.Assets.bigFont;
	}
	useScale = scale * font->glyphScale;
	if(text)
	{
		const char     *s = text;	// bk001206 - unsigned

		trap_R_SetColor(color);
		memcpy(&newColor[0], &color[0], sizeof(vec4_t));
		len = strlen(text);
		if(limit > 0 && len > limit)
		{
			len = limit;
		}
		count = 0;
		glyph2 = &font->glyphs[(int)cursor];	// bk001206 - possible signed char
		while(s && *s && count < len)
		{
			glyph = &font->glyphs[(int)*s];
			if(Q_IsColorString(s))
			{
				memcpy(newColor, g_color_table[ColorIndex(*(s + 1))], sizeof(newColor));
				newColor[3] = color[3];
				trap_R_SetColor(newColor);
				s += 2;
				continue;
			}
			else
			{
				yadj = useScale * glyph->top;
				if(style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE)
				{
					int             ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;

					colorBlack[3] = newColor[3];
					trap_R_SetColor(colorBlack);
					Text_PaintChar(x + ofs, y - yadj + ofs,
								   glyph->imageWidth,
								   glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);
					colorBlack[3] = 1.0;
					trap_R_SetColor(newColor);
				}
				else if(style == ITEM_TEXTSTYLE_NEON)
				{
					vec4_t          glow, outer, inner, white;

					glow[0] = newColor[0] * 0.5;
					glow[1] = newColor[1] * 0.5;
					glow[2] = newColor[2] * 0.5;
					glow[3] = newColor[3] * 0.2;

					outer[0] = newColor[0];
					outer[1] = newColor[1];
					outer[2] = newColor[2];
					outer[3] = newColor[3];

					inner[0] = newColor[0] * 1.5 > 1.0f ? 1.0f : newColor[0] * 1.5;
					inner[1] = newColor[1] * 1.5 > 1.0f ? 1.0f : newColor[1] * 1.5;
					inner[2] = newColor[2] * 1.5 > 1.0f ? 1.0f : newColor[2] * 1.5;
					inner[3] = newColor[3];

					white[0] = white[1] = white[2] = white[3] = 1.0f;

					trap_R_SetColor(glow);
					Text_PaintChar(x - 1.5, y - yadj - 1.5,
								   glyph->imageWidth + 3,
								   glyph->imageHeight + 3, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

					trap_R_SetColor(outer);
					Text_PaintChar(x - 1, y - yadj - 1,
								   glyph->imageWidth + 2,
								   glyph->imageHeight + 2, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

					trap_R_SetColor(inner);
					Text_PaintChar(x - 0.5, y - yadj - 0.5,
								   glyph->imageWidth + 1,
								   glyph->imageHeight + 1, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

					trap_R_SetColor(white);
				}

				Text_PaintChar(x, y - yadj,
							   glyph->imageWidth,
							   glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

				// CG_DrawPic(x, y - yadj, scale * uiDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * uiDC.Assets.textFont.glyphs[text[i]].imageHeight, uiDC.Assets.textFont.glyphs[text[i]].glyph);
				yadj = useScale * glyph2->top;
				if(count == cursorPos && !((uiInfo.uiDC.realTime / BLINK_DIVISOR) & 1))
				{
					Text_PaintChar(x, y - yadj,
								   glyph2->imageWidth,
								   glyph2->imageHeight, useScale, glyph2->s, glyph2->t, glyph2->s2, glyph2->t2, glyph2->glyph);
				}

				x += (glyph->xSkip * useScale);
				s++;
				count++;
			}
		}
		// need to paint cursor at end of text
		if(cursorPos == len && !((uiInfo.uiDC.realTime / BLINK_DIVISOR) & 1))
		{
			yadj = useScale * glyph2->top;
			Text_PaintChar(x, y - yadj,
						   glyph2->imageWidth,
						   glyph2->imageHeight, useScale, glyph2->s, glyph2->t, glyph2->s2, glyph2->t2, glyph2->glyph);

		}


		trap_R_SetColor(NULL);
	}
}


static void Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit)
{
	int             len, count;
	vec4_t          newColor;
	glyphInfo_t    *glyph;

	if(text)
	{
		const char     *s = text;	// bk001206 - unsigned
		float           max = *maxX;
		float           useScale;
		fontInfo_t     *font = &uiInfo.uiDC.Assets.textFont;

		if(scale <= ui_smallFont.value)
		{
			font = &uiInfo.uiDC.Assets.smallFont;
		}
		else if(scale > ui_bigFont.value)
		{
			font = &uiInfo.uiDC.Assets.bigFont;
		}
		useScale = scale * font->glyphScale;
		trap_R_SetColor(color);
		len = strlen(text);
		if(limit > 0 && len > limit)
		{
			len = limit;
		}
		count = 0;
		while(s && *s && count < len)
		{
			glyph = &font->glyphs[(int)*s];
			if(Q_IsColorString(s))
			{
				memcpy(newColor, g_color_table[ColorIndex(*(s + 1))], sizeof(newColor));
				newColor[3] = color[3];
				trap_R_SetColor(newColor);
				s += 2;
				continue;
			}
			else
			{
				float           yadj = useScale * glyph->top;

				if(Text_Width(s, useScale, 1) + x > max)
				{
					*maxX = 0;
					break;
				}
				Text_PaintChar(x, y - yadj,
							   glyph->imageWidth,
							   glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);
				x += (glyph->xSkip * useScale) + adjust;
				*maxX = x;
				count++;
				s++;
			}
		}
		trap_R_SetColor(NULL);
	}

}


void UI_ShowPostGame(qboolean newHigh)
{
	trap_Cvar_Set("cg_cameraOrbit", "0");
	trap_Cvar_Set("cg_thirdPerson", "0");
	trap_Cvar_Set("sv_killserver", "1");
	uiInfo.soundHighScore = newHigh;
	_UI_SetActiveMenu(UIMENU_POSTGAME);
}

/*
=================
_UI_Refresh
=================
*/

void UI_DrawCenteredPic(qhandle_t image, int w, int h)
{
	int             x, y;

	x = (SCREEN_WIDTH - w) / 2;
	y = (SCREEN_HEIGHT - h) / 2;
	UI_DrawHandlePic(x, y, w, h, image);
}

int             frameCount = 0;
int             startTime;

#define UI_FPS_FRAMES 4
void _UI_Refresh(int realtime)
{
	static int      index;
	static int      previousTimes[UI_FPS_FRAMES];

	//if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
	//  return;
	//}

	uiInfo.uiDC.frameTime = realtime - uiInfo.uiDC.realTime;
	uiInfo.uiDC.realTime = realtime;

	previousTimes[index % UI_FPS_FRAMES] = uiInfo.uiDC.frameTime;
	index++;
	if(index > UI_FPS_FRAMES)
	{
		int             i, total;

		// average multiple frames together to smooth changes out a bit
		total = 0;
		for(i = 0; i < UI_FPS_FRAMES; i++)
		{
			total += previousTimes[i];
		}
		if(!total)
		{
			total = 1;
		}
		uiInfo.uiDC.FPS = 1000 * UI_FPS_FRAMES / total;
	}



	UI_UpdateCvars();

	if(Menu_Count() > 0)
	{
		// paint all the menus
		Menu_PaintAll();
		// refresh server browser list
		UI_DoServerRefresh();
		// refresh server status
		UI_BuildServerStatus(qfalse);
		// refresh find player list
		UI_BuildFindPlayerList(qfalse);
	}

	// draw cursor
	UI_SetColor(NULL);

	//TA: don't draw the cursor whilst loading
	if(Menu_Count() > 0 && !trap_Cvar_VariableValue("ui_loading"))
		UI_DrawHandlePic(uiInfo.uiDC.cursorx - 16, uiInfo.uiDC.cursory - 16, 32, 32, uiInfo.uiDC.Assets.cursor);

#ifndef NDEBUG
	if(uiInfo.uiDC.debug)
	{
		// cursor coordinates
		//FIXME
		//UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed );
	}
#endif

}

/*
=================
_UI_Shutdown
=================
*/
void _UI_Shutdown(void)
{
	trap_LAN_SaveCachedServers();
}

char           *defaultMenu = NULL;

char           *GetMenuBuffer(const char *filename)
{
	int             len;
	fileHandle_t    f;
	static char     buf[MAX_MENUFILE];

	len = trap_FS_FOpenFile(filename, &f, FS_READ);
	if(!f)
	{
		trap_Print(va(S_COLOR_RED "menu file not found: %s, using default\n", filename));
		return defaultMenu;
	}
	if(len >= MAX_MENUFILE)
	{
		trap_Print(va(S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", filename, len, MAX_MENUFILE));
		trap_FS_FCloseFile(f);
		return defaultMenu;
	}

	trap_FS_Read(buf, len, f);
	buf[len] = 0;
	trap_FS_FCloseFile(f);
	//Com_Compress(buf);
	return buf;

}

qboolean Asset_Parse(int handle)
{
	pc_token_t      token;
	const char     *tempStr;

	if(!trap_PC_ReadToken(handle, &token))
		return qfalse;
	if(Q_stricmp(token.string, "{") != 0)
	{
		return qfalse;
	}

	while(1)
	{

		memset(&token, 0, sizeof(pc_token_t));

		if(!trap_PC_ReadToken(handle, &token))
			return qfalse;

		if(Q_stricmp(token.string, "}") == 0)
		{
			return qtrue;
		}

		// font
		if(Q_stricmp(token.string, "font") == 0)
		{
			int             pointSize;

			if(!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize))
			{
				return qfalse;
			}
			trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.textFont);
			uiInfo.uiDC.Assets.fontRegistered = qtrue;
			continue;
		}

		if(Q_stricmp(token.string, "smallFont") == 0)
		{
			int             pointSize;

			if(!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize))
			{
				return qfalse;
			}
			trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.smallFont);
			continue;
		}

		if(Q_stricmp(token.string, "bigFont") == 0)
		{
			int             pointSize;

			if(!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize))
			{
				return qfalse;
			}
			trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.bigFont);
			continue;
		}


		// gradientbar
		if(Q_stricmp(token.string, "gradientbar") == 0)
		{
			if(!PC_String_Parse(handle, &tempStr))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr);
			continue;
		}

		// enterMenuSound
		if(Q_stricmp(token.string, "menuEnterSound") == 0)
		{
			if(!PC_String_Parse(handle, &tempStr))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.menuEnterSound = trap_S_RegisterSound(tempStr, qfalse);
			continue;
		}

		// exitMenuSound
		if(Q_stricmp(token.string, "menuExitSound") == 0)
		{
			if(!PC_String_Parse(handle, &tempStr))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.menuExitSound = trap_S_RegisterSound(tempStr, qfalse);
			continue;
		}

		// itemFocusSound
		if(Q_stricmp(token.string, "itemFocusSound") == 0)
		{
			if(!PC_String_Parse(handle, &tempStr))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.itemFocusSound = trap_S_RegisterSound(tempStr, qfalse);
			continue;
		}

		// menuBuzzSound
		if(Q_stricmp(token.string, "menuBuzzSound") == 0)
		{
			if(!PC_String_Parse(handle, &tempStr))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.menuBuzzSound = trap_S_RegisterSound(tempStr, qfalse);
			continue;
		}

		if(Q_stricmp(token.string, "cursor") == 0)
		{
			if(!PC_String_Parse(handle, &uiInfo.uiDC.Assets.cursorStr))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip(uiInfo.uiDC.Assets.cursorStr);
			continue;
		}

		if(Q_stricmp(token.string, "fadeClamp") == 0)
		{
			if(!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeClamp))
			{
				return qfalse;
			}
			continue;
		}

		if(Q_stricmp(token.string, "fadeCycle") == 0)
		{
			if(!PC_Int_Parse(handle, &uiInfo.uiDC.Assets.fadeCycle))
			{
				return qfalse;
			}
			continue;
		}

		if(Q_stricmp(token.string, "fadeAmount") == 0)
		{
			if(!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeAmount))
			{
				return qfalse;
			}
			continue;
		}

		if(Q_stricmp(token.string, "shadowX") == 0)
		{
			if(!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowX))
			{
				return qfalse;
			}
			continue;
		}

		if(Q_stricmp(token.string, "shadowY") == 0)
		{
			if(!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowY))
			{
				return qfalse;
			}
			continue;
		}

		if(Q_stricmp(token.string, "shadowColor") == 0)
		{
			if(!PC_Color_Parse(handle, &uiInfo.uiDC.Assets.shadowColor))
			{
				return qfalse;
			}
			uiInfo.uiDC.Assets.shadowFadeClamp = uiInfo.uiDC.Assets.shadowColor[3];
			continue;
		}

	}
	return qfalse;
}

void Font_Report(void)
{
	int             i;

	Com_Printf("Font Info\n");
	Com_Printf("=========\n");
	for(i = 32; i < 96; i++)
	{
		Com_Printf("Glyph handle %i: %i\n", i, uiInfo.uiDC.Assets.textFont.glyphs[i].glyph);
	}
}

void UI_Report(void)
{
	String_Report();
	//Font_Report();

}

void UI_ParseMenu(const char *menuFile)
{
	int             handle;
	pc_token_t      token;

	//Com_Printf("Parsing menu file:%s\n", menuFile);

	handle = trap_PC_LoadSource(menuFile);
	if(!handle)
	{
		Com_Printf("Menu parse failed:%s\n", menuFile);
		return;
	}

	while(1)
	{
		memset(&token, 0, sizeof(pc_token_t));
		if(!trap_PC_ReadToken(handle, &token))
		{
			break;
		}

		if(token.string[0] == '}')
		{
			break;
		}

		if(Q_stricmp(token.string, "assetGlobalDef") == 0)
		{
			if(Asset_Parse(handle))
			{
				continue;
			}
			else
			{
				break;
			}
		}

		if(Q_stricmp(token.string, "menudef") == 0)
		{
			// start a new menu
			Menu_New(handle);
		}
	}
	trap_PC_FreeSource(handle);
}

/*
===============
UI_FindInfoPaneByName
===============
*/
tremInfoPane_t *UI_FindInfoPaneByName(const char *name)
{
	int             i;

	for(i = 0; i < uiInfo.tremInfoPaneCount; i++)
	{
		if(!Q_stricmp(uiInfo.tremInfoPanes[i].name, name))
			return &uiInfo.tremInfoPanes[i];
	}

	//create a dummy infopane demanding the user write the infopane
	uiInfo.tremInfoPanes[i].name = String_Alloc(name);
	strncpy(uiInfo.tremInfoPanes[i].text, "Not implemented.\n\nui/infopanes.def\n", MAX_INFOPANE_TEXT);
	Q_strcat(uiInfo.tremInfoPanes[i].text, MAX_INFOPANE_TEXT, String_Alloc(name));

	uiInfo.tremInfoPaneCount++;

	return &uiInfo.tremInfoPanes[i];
}

/*
===============
UI_LoadInfoPane
===============
*/
qboolean UI_LoadInfoPane(int handle)
{
	pc_token_t      token;
	qboolean        valid = qfalse;

	while(1)
	{
		memset(&token, 0, sizeof(pc_token_t));

		if(!trap_PC_ReadToken(handle, &token))
			break;

		if(!Q_stricmp(token.string, "name"))
		{
			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].name = String_Alloc(token.string);
			valid = qtrue;
		}
		else if(!Q_stricmp(token.string, "graphic"))
		{
			int            *graphic;

			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			graphic = &uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].numGraphics;

			if(!Q_stricmp(token.string, "top"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].side = INFOPANE_TOP;
			else if(!Q_stricmp(token.string, "bottom"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].side = INFOPANE_BOTTOM;
			else if(!Q_stricmp(token.string, "left"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].side = INFOPANE_LEFT;
			else if(!Q_stricmp(token.string, "right"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].side = INFOPANE_RIGHT;
			else
				break;

			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			if(!Q_stricmp(token.string, "center"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].offset = -1;
			else
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].offset = token.intvalue;

			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].graphic = trap_R_RegisterShaderNoMip(token.string);

			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].width = token.intvalue;

			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].graphics[*graphic].height = token.intvalue;

			//increment graphics
			(*graphic)++;

			if(*graphic == MAX_INFOPANE_GRAPHICS)
				break;
		}
		else if(!Q_stricmp(token.string, "text"))
		{
			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			Q_strcat(uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].text, MAX_INFOPANE_TEXT, token.string);
		}
		else if(!Q_stricmp(token.string, "align"))
		{
			memset(&token, 0, sizeof(pc_token_t));

			if(!trap_PC_ReadToken(handle, &token))
				break;

			if(!Q_stricmp(token.string, "left"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].align = ITEM_ALIGN_LEFT;
			else if(!Q_stricmp(token.string, "right"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].align = ITEM_ALIGN_RIGHT;
			else if(!Q_stricmp(token.string, "center"))
				uiInfo.tremInfoPanes[uiInfo.tremInfoPaneCount].align = ITEM_ALIGN_CENTER;
		}
		else if(token.string[0] == '}')
		{
			//reached the end, break
			break;
		}
		else
			break;
	}

	if(valid)
	{
		uiInfo.tremInfoPaneCount++;
		return qtrue;
	}
	else
	{
		return qfalse;
	}
}

/*
===============
UI_LoadInfoPanes
===============
*/
void UI_LoadInfoPanes(const char *file)
{
	pc_token_t      token;
	int             handle;
	int             count;

	uiInfo.tremInfoPaneCount = count = 0;

	handle = trap_PC_LoadSource(file);

	if(!handle)
	{
		trap_Error(va(S_COLOR_YELLOW "infopane file not found: %s\n", file));
		return;
	}

	while(1)
	{
		if(!trap_PC_ReadToken(handle, &token))
			break;

		if(token.string[0] == 0)
			break;

		if(token.string[0] == '{')
		{
			if(UI_LoadInfoPane(handle))
				count++;

			if(count == MAX_INFOPANES)
				break;
		}
	}

	trap_PC_FreeSource(handle);
}

qboolean UI_Load_Menu(char **text)
{
        char           *token;

        token = Com_ParseExt(text, qtrue);

        if(token[0] != '{')
                return qfalse;

        while(1)
        {
                token = Com_ParseExt(text, qtrue);

                if(Q_stricmp(token, "}") == 0)
                        return qtrue;

                if(!token || token[0] == 0)
                        return qfalse;

		//Com_Printf("UI_Load_Menu:%s\n", token);
		UI_ParseMenu(token);
        }
        return qfalse;
}

void UI_LoadMenus(const char *menuFile, qboolean reset)
{
        char           *p, *prev;
        int             len, start;
        char           *token;
        int             skip;
        char            buf[20000];
        fileHandle_t    f;

	//Com_Printf("UI_LoadMenus: Loading menus from %s\n", menuFile);

        start = trap_Milliseconds();

        len = trap_FS_FOpenFile(menuFile, &f, FS_READ);
        if(!f)
        {
                trap_Error(va(S_COLOR_RED "UI_LoadMenus: menu file not found: %s, using default\n", menuFile));
        }

        if(len >= MAX_MENUDEFFILE)
        {
                trap_Error(va(S_COLOR_RED "UI_LoadMenus: menu file too large: %s is %i, max allowed is %i", menuFile, len, MAX_MENUDEFFILE));
                trap_FS_FCloseFile(f);
                return;
        }

        trap_FS_Read(buf, len, f);
        buf[len] = 0;
        trap_FS_FCloseFile(f);

	Com_Compress(buf);

        // parse the text
        p = buf;
        skip = 0;                                       // quite the compiler warning

	token = Com_ParseExt(&p, qtrue);
        if(token[0] != '{')
        {
		Com_Printf("WARNING: couldn't parse menus from '%s'\n", menuFile);
		return;
	}

	ui_new.integer = 1;
	if(reset)
	{
		Menu_Reset();
	}

        while(1)
        {
                prev = p;                  // so we can unget
                token = Com_Parse(&p);
                if(!token[0] || token[0] == '}')
                {
                        break;
                }

		if(!Q_stricmp(token, "loadmenu"))
		{
			if (UI_Load_Menu(&p))
				continue;
			else
				break;
		}
	}

        Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start);
}

void UI_Load(void)
{
	char            lastName[1024];
	menuDef_t      *menu = Menu_GetFocused();
	char           *menuSet = UI_Cvar_VariableString("ui_menuFiles");

	if(menu && menu->window.name)
	{
		strcpy(lastName, menu->window.name);
	}
	if(menuSet == NULL || menuSet[0] == '\0')
	{
		menuSet = "ui/menus.txt";
	}

	String_Init();

/*  UI_ParseGameInfo("gameinfo.txt");
  UI_LoadArenas();*/

	UI_LoadMenus(menuSet, qtrue);
	Menus_CloseAll();
	Menus_ActivateByName(lastName);

}

static const char *handicapValues[] =
	{ "None", "95", "90", "85", "80", "75", "70", "65", "60", "55", "50", "45", "40", "35", "30", "25", "20", "15", "10", "5",
	NULL
};

static void UI_DrawHandicap(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             i, h;

	h = Com_Clamp(5, 100, trap_Cvar_VariableValue("handicap"));
	i = 20 - h / 5;

	Text_Paint(rect->x, rect->y, scale, color, handicapValues[i], 0, 0, textStyle);
}

static void UI_DrawClanName(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_teamName"), 0, 0, textStyle);
}


static void UI_SetCapFragLimits(qboolean uiVars)
{
	int             cap = 5;
	int             frag = 10;

	if(uiVars)
	{
		trap_Cvar_Set("ui_captureLimit", va("%d", cap));
		trap_Cvar_Set("ui_fragLimit", va("%d", frag));
	}
	else
	{
		trap_Cvar_Set("capturelimit", va("%d", cap));
		trap_Cvar_Set("fraglimit", va("%d", frag));
	}
}

// ui_gameType assumes gametype 0 is -1 ALL and will not show
static void UI_DrawGameType(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_gameType.integer].gameType, 0, 0, textStyle);
}

static void UI_DrawNetGameType(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(ui_netGameType.integer < 0 || ui_netGameType.integer > uiInfo.numGameTypes)
	{
		trap_Cvar_Set("ui_netGameType", "0");
		trap_Cvar_Set("ui_actualNetGameType", "0");
	}
	Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_netGameType.integer].gameType, 0, 0, textStyle);
}

static void UI_DrawJoinGameType(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(ui_joinGameType.integer < 0 || ui_joinGameType.integer > uiInfo.numJoinGameTypes)
	{
		trap_Cvar_Set("ui_joinGameType", "0");
	}
	Text_Paint(rect->x, rect->y, scale, color, uiInfo.joinGameTypes[ui_joinGameType.integer].gameType, 0, 0, textStyle);
}



static int UI_TeamIndexFromName(const char *name)
{
	int             i;

	if(name && *name)
	{
		for(i = 0; i < uiInfo.teamCount; i++)
		{
			if(Q_stricmp(name, uiInfo.teamList[i].teamName) == 0)
			{
				return i;
			}
		}
	}

	return 0;

}

static void UI_DrawClanLogo(rectDef_t * rect, float scale, vec4_t color)
{
	int             i;

	i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
	if(i >= 0 && i < uiInfo.teamCount)
	{
		trap_R_SetColor(color);

		if(uiInfo.teamList[i].teamIcon == -1)
		{
			uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
			uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
			uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
		}

		UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon);
		trap_R_SetColor(NULL);
	}
}

static void UI_DrawClanCinematic(rectDef_t * rect, float scale, vec4_t color)
{
	int             i;

	i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
	if(i >= 0 && i < uiInfo.teamCount)
	{

		if(uiInfo.teamList[i].cinematic >= -2)
		{
			if(uiInfo.teamList[i].cinematic == -1)
			{
				uiInfo.teamList[i].cinematic =
					trap_CIN_PlayCinematic(va("%s.roq", uiInfo.teamList[i].imageName), 0, 0, 0, 0, (CIN_loop | CIN_silent));
			}
			if(uiInfo.teamList[i].cinematic >= 0)
			{
				trap_CIN_RunCinematic(uiInfo.teamList[i].cinematic);
				trap_CIN_SetExtents(uiInfo.teamList[i].cinematic, rect->x, rect->y, rect->w, rect->h);
				trap_CIN_DrawCinematic(uiInfo.teamList[i].cinematic);
			}
			else
			{
				trap_R_SetColor(color);
				UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal);
				trap_R_SetColor(NULL);
				uiInfo.teamList[i].cinematic = -2;
			}
		}
		else
		{
			trap_R_SetColor(color);
			UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon);
			trap_R_SetColor(NULL);
		}
	}

}

static void UI_DrawPreviewCinematic(rectDef_t * rect, float scale, vec4_t color)
{
	if(uiInfo.previewMovie > -2)
	{
		uiInfo.previewMovie =
			trap_CIN_PlayCinematic(va("%s.roq", uiInfo.movieList[uiInfo.movieIndex]), 0, 0, 0, 0, (CIN_loop | CIN_silent));
		if(uiInfo.previewMovie >= 0)
		{
			trap_CIN_RunCinematic(uiInfo.previewMovie);
			trap_CIN_SetExtents(uiInfo.previewMovie, rect->x, rect->y, rect->w, rect->h);
			trap_CIN_DrawCinematic(uiInfo.previewMovie);
		}
		else
		{
			uiInfo.previewMovie = -2;
		}
	}

}


#define GRAPHIC_BWIDTH  8.0f
/*
===============
UI_DrawInfoPane
===============
*/
static void UI_DrawInfoPane(tremInfoPane_t * pane, rectDef_t * rect, float text_x, float text_y,
							float scale, vec4_t color, int textStyle)
{
	int             i;
	float           maxLeft = 0, maxTop = 0;
	float           maxRight = 0, maxBottom = 0;
	float           x = rect->x - text_x, y = rect->y - text_y, w, h;
	float           xoffset = 0, yoffset = 0;
	menuDef_t       dummyParent;
	itemDef_t       textItem;

	//iterate through graphics
	for(i = 0; i < pane->numGraphics; i++)
	{
		float           width = pane->graphics[i].width;
		float           height = pane->graphics[i].height;
		qhandle_t       graphic = pane->graphics[i].graphic;

		if(pane->graphics[i].side == INFOPANE_TOP || pane->graphics[i].side == INFOPANE_BOTTOM)
		{
			//set horizontal offset of graphic
			if(pane->graphics[i].offset < 0)
				xoffset = (rect->w / 2) - (pane->graphics[i].width / 2);
			else
				xoffset = pane->graphics[i].offset + GRAPHIC_BWIDTH;
		}
		else if(pane->graphics[i].side == INFOPANE_LEFT || pane->graphics[i].side == INFOPANE_RIGHT)
		{
			//set vertical offset of graphic
			if(pane->graphics[i].offset < 0)
				yoffset = (rect->h / 2) - (pane->graphics[i].height / 2);
			else
				yoffset = pane->graphics[i].offset + GRAPHIC_BWIDTH;
		}

		if(pane->graphics[i].side == INFOPANE_LEFT)
		{
			//set the horizontal offset of the text
			if(pane->graphics[i].width > maxLeft)
				maxLeft = pane->graphics[i].width + GRAPHIC_BWIDTH;

			xoffset = GRAPHIC_BWIDTH;
		}
		else if(pane->graphics[i].side == INFOPANE_RIGHT)
		{
			if(pane->graphics[i].width > maxRight)
				maxRight = pane->graphics[i].width + GRAPHIC_BWIDTH;

			xoffset = rect->w - width - GRAPHIC_BWIDTH;
		}
		else if(pane->graphics[i].side == INFOPANE_TOP)
		{
			//set the vertical offset of the text
			if(pane->graphics[i].height > maxTop)
				maxTop = pane->graphics[i].height + GRAPHIC_BWIDTH;

			yoffset = GRAPHIC_BWIDTH;
		}
		else if(pane->graphics[i].side == INFOPANE_BOTTOM)
		{
			if(pane->graphics[i].height > maxBottom)
				maxBottom = pane->graphics[i].height + GRAPHIC_BWIDTH;

			yoffset = rect->h - height - GRAPHIC_BWIDTH;
		}

		//draw the graphic
		UI_DrawHandlePic(x + xoffset, y + yoffset, width, height, graphic);
	}

	//offset the text
	x = rect->x + maxLeft;
	y = rect->y + maxTop;
	w = rect->w - (maxLeft + maxRight + 16 + (2 * text_x));	//16 to ensure text within frame
	h = rect->h - (maxTop + maxBottom);

	textItem.text = pane->text;

	textItem.parent = &dummyParent;
	memcpy(textItem.window.foreColor, color, sizeof(vec4_t));
	textItem.window.flags = 0;

	switch (pane->align)
	{
		case ITEM_ALIGN_LEFT:
			textItem.window.rect.x = x;
			break;

		case ITEM_ALIGN_RIGHT:
			textItem.window.rect.x = x + w;
			break;

		case ITEM_ALIGN_CENTER:
			textItem.window.rect.x = x + (w / 2);
			break;

		default:
			textItem.window.rect.x = x;
			break;
	}

	textItem.window.rect.y = y;
	textItem.window.rect.w = w;
	textItem.window.rect.h = h;
	textItem.window.borderSize = 0;
	textItem.textRect.x = 0;
	textItem.textRect.y = 0;
	textItem.textRect.w = 0;
	textItem.textRect.h = 0;
	textItem.textalignment = pane->align;
	textItem.textalignx = text_x;
	textItem.textaligny = text_y;
	textItem.textscale = scale;
	textItem.textStyle = textStyle;

	textItem.enableCvar = NULL;
	textItem.cvarTest = NULL;

	//hack to utilise existing autowrap code
	Item_Text_AutoWrapped_Paint(&textItem);
}


static void UI_DrawSkill(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             i;

	i = trap_Cvar_VariableValue("g_spSkill");
	if(i < 1 || i > numSkillLevels)
	{
		i = 1;
	}
	Text_Paint(rect->x, rect->y, scale, color, skillLevels[i - 1], 0, 0, textStyle);
}


static void UI_DrawTeamName(rectDef_t * rect, float scale, vec4_t color, qboolean blue, int textStyle)
{
	int             i;

	i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam"));
	if(i >= 0 && i < uiInfo.teamCount)
	{
		Text_Paint(rect->x, rect->y, scale, color, va("%s: %s", (blue) ? "Blue" : "Red", uiInfo.teamList[i].teamName), 0, 0,
				   textStyle);
	}
}

static void UI_DrawTeamMember(rectDef_t * rect, float scale, vec4_t color, qboolean blue, int num, int textStyle)
{
	// 0 - None
	// 1 - Human
	// 2..NumCharacters - Bot
	int             value = trap_Cvar_VariableValue(va(blue ? "ui_blueteam%i" : "ui_redteam%i", num));
	const char     *text;

	if(value <= 0)
	{
		text = "Closed";
	}
	else if(value == 1)
	{
		text = "Human";
	}
	else
	{
		value -= 2;

		if(value >= UI_GetNumBots())
			value = 0;

		text = UI_GetBotNameByNumber(value);
	}
	Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle);
}

static void UI_DrawMapPreview(rectDef_t * rect, float scale, vec4_t color, qboolean net)
{
	int             map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer;

	if(map < 0 || map > uiInfo.mapCount)
	{
		if(net)
		{
			ui_currentNetMap.integer = 0;
			trap_Cvar_Set("ui_currentNetMap", "0");
		}
		else
		{
			ui_currentMap.integer = 0;
			trap_Cvar_Set("ui_currentMap", "0");
		}
		map = 0;
	}

	if(uiInfo.mapList[map].levelShot == -1)
	{
		uiInfo.mapList[map].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[map].imageName);
	}

	if(uiInfo.mapList[map].levelShot > 0)
	{
		UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.mapList[map].levelShot);
	}
	else
	{
		UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("ui/assets/connecting"));
	}
}


static void UI_DrawMapTimeToBeat(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             minutes, seconds, time;

	if(ui_currentMap.integer < 0 || ui_currentMap.integer > uiInfo.mapCount)
	{
		ui_currentMap.integer = 0;
		trap_Cvar_Set("ui_currentMap", "0");
	}

	time = uiInfo.mapList[ui_currentMap.integer].timeToBeat[uiInfo.gameTypes[ui_gameType.integer].gtEnum];

	minutes = time / 60;
	seconds = time % 60;

	Text_Paint(rect->x, rect->y, scale, color, va("%02i:%02i", minutes, seconds), 0, 0, textStyle);
}



static void UI_DrawMapCinematic(rectDef_t * rect, float scale, vec4_t color, qboolean net)
{

	int             map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer;

	if(map < 0 || map > uiInfo.mapCount)
	{
		if(net)
		{
			ui_currentNetMap.integer = 0;
			trap_Cvar_Set("ui_currentNetMap", "0");
		}
		else
		{
			ui_currentMap.integer = 0;
			trap_Cvar_Set("ui_currentMap", "0");
		}
		map = 0;
	}

	if(uiInfo.mapList[map].cinematic >= -1)
	{
		if(uiInfo.mapList[map].cinematic == -1)
		{
			uiInfo.mapList[map].cinematic =
				trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[map].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent));
		}
		if(uiInfo.mapList[map].cinematic >= 0)
		{
			trap_CIN_RunCinematic(uiInfo.mapList[map].cinematic);
			trap_CIN_SetExtents(uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h);
			trap_CIN_DrawCinematic(uiInfo.mapList[map].cinematic);
		}
		else
		{
			uiInfo.mapList[map].cinematic = -2;
		}
	}
	else
	{
		UI_DrawMapPreview(rect, scale, color, net);
	}
}



static qboolean updateModel = qtrue;
static qboolean q3Model = qfalse;

static void UI_DrawPlayerModel(rectDef_t * rect)
{
	static playerInfo_t info;
	char            model[MAX_QPATH];
	char            team[256];
	char            head[256];
	vec3_t          viewangles;
	vec3_t          moveangles;

	if(trap_Cvar_VariableValue("ui_Q3Model"))
	{
		strcpy(model, UI_Cvar_VariableString("model"));
		strcpy(head, UI_Cvar_VariableString("headmodel"));
		if(!q3Model)
		{
			q3Model = qtrue;
			updateModel = qtrue;
		}
		team[0] = '\0';
	}
	else
	{

		strcpy(team, UI_Cvar_VariableString("ui_teamName"));
		strcpy(model, UI_Cvar_VariableString("team_model"));
		strcpy(head, UI_Cvar_VariableString("team_headmodel"));
		if(q3Model)
		{
			q3Model = qfalse;
			updateModel = qtrue;
		}
	}
	if(updateModel)
	{
		memset(&info, 0, sizeof(playerInfo_t));
		viewangles[YAW] = 180 - 10;
		viewangles[PITCH] = 0;
		viewangles[ROLL] = 0;
		VectorClear(moveangles);
		UI_PlayerInfo_SetModel(&info, model, head, team);
		UI_PlayerInfo_SetInfo(&info, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse);
//    UI_RegisterClientModelname( &info, model, head, team);
		updateModel = qfalse;
	}

	UI_DrawPlayer(rect->x, rect->y, rect->w, rect->h, &info, uiInfo.uiDC.realTime / 2);

}

static void UI_DrawNetSource(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(ui_netSource.integer < 0 || ui_netSource.integer > numNetSources)
	{
		ui_netSource.integer = 0;
	}
	Text_Paint(rect->x, rect->y, scale, color, va("Source: %s", netSources[ui_netSource.integer]), 0, 0, textStyle);
}

static void UI_DrawNetMapPreview(rectDef_t * rect, float scale, vec4_t color)
{

	if(uiInfo.serverStatus.currentServerPreview > 0)
	{
		UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.serverStatus.currentServerPreview);
	}
	else
	{
		UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("ui/assets/connecting"));
	}
}

static void UI_DrawNetMapCinematic(rectDef_t * rect, float scale, vec4_t color)
{
	if(ui_currentNetMap.integer < 0 || ui_currentNetMap.integer > uiInfo.mapCount)
	{
		ui_currentNetMap.integer = 0;
		trap_Cvar_Set("ui_currentNetMap", "0");
	}

	if(uiInfo.serverStatus.currentServerCinematic >= 0)
	{
		trap_CIN_RunCinematic(uiInfo.serverStatus.currentServerCinematic);
		trap_CIN_SetExtents(uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h);
		trap_CIN_DrawCinematic(uiInfo.serverStatus.currentServerCinematic);
	}
	else
	{
		UI_DrawNetMapPreview(rect, scale, color);
	}
}



static void UI_DrawNetFilter(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters)
	{
		ui_serverFilterType.integer = 0;
	}
	Text_Paint(rect->x, rect->y, scale, color, va("Filter: %s", serverFilters[ui_serverFilterType.integer].description), 0, 0,
			   textStyle);
}


static void UI_DrawTier(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             i;

	i = trap_Cvar_VariableValue("ui_currentTier");
	if(i < 0 || i >= uiInfo.tierCount)
	{
		i = 0;
	}
	Text_Paint(rect->x, rect->y, scale, color, va("Tier: %s", uiInfo.tierList[i].tierName), 0, 0, textStyle);
}

static void UI_DrawTierMap(rectDef_t * rect, int index)
{
	int             i;

	i = trap_Cvar_VariableValue("ui_currentTier");
	if(i < 0 || i >= uiInfo.tierCount)
	{
		i = 0;
	}

	if(uiInfo.tierList[i].mapHandles[index] == -1)
	{
		uiInfo.tierList[i].mapHandles[index] = trap_R_RegisterShaderNoMip(va("levelshots/%s", uiInfo.tierList[i].maps[index]));
	}

	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.tierList[i].mapHandles[index]);
}

static const char *UI_EnglishMapName(const char *map)
{
	int             i;

	for(i = 0; i < uiInfo.mapCount; i++)
	{
		if(Q_stricmp(map, uiInfo.mapList[i].mapLoadName) == 0)
		{
			return uiInfo.mapList[i].mapName;
		}
	}
	return "";
}

static void UI_DrawTierMapName(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             i, j;

	i = trap_Cvar_VariableValue("ui_currentTier");
	if(i < 0 || i >= uiInfo.tierCount)
	{
		i = 0;
	}
	j = trap_Cvar_VariableValue("ui_currentMap");
	if(j < 0 || j > MAPS_PER_TIER)
	{
		j = 0;
	}

	Text_Paint(rect->x, rect->y, scale, color, UI_EnglishMapName(uiInfo.tierList[i].maps[j]), 0, 0, textStyle);
}

static void UI_DrawTierGameType(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             i, j;

	i = trap_Cvar_VariableValue("ui_currentTier");
	if(i < 0 || i >= uiInfo.tierCount)
	{
		i = 0;
	}
	j = trap_Cvar_VariableValue("ui_currentMap");
	if(j < 0 || j > MAPS_PER_TIER)
	{
		j = 0;
	}

	Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[uiInfo.tierList[i].gameTypes[j]].gameType, 0, 0, textStyle);
}


static const char *UI_AIFromName(const char *name)
{
	int             j;

	for(j = 0; j < uiInfo.aliasCount; j++)
	{
		if(Q_stricmp(uiInfo.aliasList[j].name, name) == 0)
		{
			return uiInfo.aliasList[j].ai;
		}
	}
	return "James";
}

static qboolean updateOpponentModel = qtrue;
static void UI_DrawOpponent(rectDef_t * rect)
{
	static playerInfo_t info2;
	char            model[MAX_QPATH];
	char            headmodel[MAX_QPATH];
	char            team[256];
	vec3_t          viewangles;
	vec3_t          moveangles;

	if(updateOpponentModel)
	{

		strcpy(model, UI_Cvar_VariableString("ui_opponentModel"));
		strcpy(headmodel, UI_Cvar_VariableString("ui_opponentModel"));
		team[0] = '\0';

		memset(&info2, 0, sizeof(playerInfo_t));
		viewangles[YAW] = 180 - 10;
		viewangles[PITCH] = 0;
		viewangles[ROLL] = 0;
		VectorClear(moveangles);
		UI_PlayerInfo_SetModel(&info2, model, headmodel, "");
		UI_PlayerInfo_SetInfo(&info2, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse);
		UI_RegisterClientModelname(&info2, model, headmodel, team);
		updateOpponentModel = qfalse;
	}

	UI_DrawPlayer(rect->x, rect->y, rect->w, rect->h, &info2, uiInfo.uiDC.realTime / 2);

}

static void UI_NextOpponent(void)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
	int             j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

	i++;
	if(i >= uiInfo.teamCount)
	{
		i = 0;
	}
	if(i == j)
	{
		i++;
		if(i >= uiInfo.teamCount)
		{
			i = 0;
		}
	}
	trap_Cvar_Set("ui_opponentName", uiInfo.teamList[i].teamName);
}

static void UI_PriorOpponent(void)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
	int             j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

	i--;
	if(i < 0)
	{
		i = uiInfo.teamCount - 1;
	}
	if(i == j)
	{
		i--;
		if(i < 0)
		{
			i = uiInfo.teamCount - 1;
		}
	}
	trap_Cvar_Set("ui_opponentName", uiInfo.teamList[i].teamName);
}

static void UI_DrawPlayerLogo(rectDef_t * rect, vec3_t color)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

	if(uiInfo.teamList[i].teamIcon == -1)
	{
		uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
		uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
		uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
	}

	trap_R_SetColor(color);
	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon);
	trap_R_SetColor(NULL);
}

static void UI_DrawPlayerLogoMetal(rectDef_t * rect, vec3_t color)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

	if(uiInfo.teamList[i].teamIcon == -1)
	{
		uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
		uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
		uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
	}

	trap_R_SetColor(color);
	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal);
	trap_R_SetColor(NULL);
}

static void UI_DrawPlayerLogoName(rectDef_t * rect, vec3_t color)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

	if(uiInfo.teamList[i].teamIcon == -1)
	{
		uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
		uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
		uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
	}

	trap_R_SetColor(color);
	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name);
	trap_R_SetColor(NULL);
}

static void UI_DrawOpponentLogo(rectDef_t * rect, vec3_t color)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));

	if(uiInfo.teamList[i].teamIcon == -1)
	{
		uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
		uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
		uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
	}

	trap_R_SetColor(color);
	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon);
	trap_R_SetColor(NULL);
}

static void UI_DrawOpponentLogoMetal(rectDef_t * rect, vec3_t color)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));

	if(uiInfo.teamList[i].teamIcon == -1)
	{
		uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
		uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
		uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
	}

	trap_R_SetColor(color);
	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal);
	trap_R_SetColor(NULL);
}

static void UI_DrawOpponentLogoName(rectDef_t * rect, vec3_t color)
{
	int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));

	if(uiInfo.teamList[i].teamIcon == -1)
	{
		uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
		uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal", uiInfo.teamList[i].imageName));
		uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
	}

	trap_R_SetColor(color);
	UI_DrawHandlePic(rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name);
	trap_R_SetColor(NULL);
}

static void UI_DrawAllMapsSelection(rectDef_t * rect, float scale, vec4_t color, int textStyle, qboolean net)
{
	int             map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer;

	if(map >= 0 && map < uiInfo.mapCount)
	{
		Text_Paint(rect->x, rect->y, scale, color, uiInfo.mapList[map].mapName, 0, 0, textStyle);
	}
}

static void UI_DrawOpponentName(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_opponentName"), 0, 0, textStyle);
}


static int UI_OwnerDrawWidth(int ownerDraw, float scale)
{
	int             i, h, value;
	const char     *text;
	const char     *s = NULL;

	switch (ownerDraw)
	{
		case UI_HANDICAP:
			h = Com_Clamp(5, 100, trap_Cvar_VariableValue("handicap"));
			i = 20 - h / 5;
			s = handicapValues[i];
			break;
		case UI_CLANNAME:
			s = UI_Cvar_VariableString("ui_teamName");
			break;
		case UI_GAMETYPE:
			s = uiInfo.gameTypes[ui_gameType.integer].gameType;
			break;
		case UI_SKILL:
			i = trap_Cvar_VariableValue("g_spSkill");
			if(i < 1 || i > numSkillLevels)
			{
				i = 1;
			}
			s = skillLevels[i - 1];
			break;
		case UI_BLUETEAMNAME:
			i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_blueTeam"));
			if(i >= 0 && i < uiInfo.teamCount)
			{
				s = va("%s: %s", "Blue", uiInfo.teamList[i].teamName);
			}
			break;
		case UI_REDTEAMNAME:
			i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_redTeam"));
			if(i >= 0 && i < uiInfo.teamCount)
			{
				s = va("%s: %s", "Red", uiInfo.teamList[i].teamName);
			}
			break;
		case UI_BLUETEAM1:
		case UI_BLUETEAM2:
		case UI_BLUETEAM3:
		case UI_BLUETEAM4:
		case UI_BLUETEAM5:
			value = trap_Cvar_VariableValue(va("ui_blueteam%i", ownerDraw - UI_BLUETEAM1 + 1));
			if(value <= 0)
			{
				text = "Closed";
			}
			else if(value == 1)
			{
				text = "Human";
			}
			else
			{
				value -= 2;
				if(value >= uiInfo.aliasCount)
				{
					value = 0;
				}
				text = uiInfo.aliasList[value].name;
			}
			s = va("%i. %s", ownerDraw - UI_BLUETEAM1 + 1, text);
			break;
		case UI_REDTEAM1:
		case UI_REDTEAM2:
		case UI_REDTEAM3:
		case UI_REDTEAM4:
		case UI_REDTEAM5:
			value = trap_Cvar_VariableValue(va("ui_redteam%i", ownerDraw - UI_REDTEAM1 + 1));
			if(value <= 0)
			{
				text = "Closed";
			}
			else if(value == 1)
			{
				text = "Human";
			}
			else
			{
				value -= 2;
				if(value >= uiInfo.aliasCount)
				{
					value = 0;
				}
				text = uiInfo.aliasList[value].name;
			}
			s = va("%i. %s", ownerDraw - UI_REDTEAM1 + 1, text);
			break;
		case UI_NETSOURCE:
			if(ui_netSource.integer < 0 || ui_netSource.integer > uiInfo.numJoinGameTypes)
			{
				ui_netSource.integer = 0;
			}
			s = va("Source: %s", netSources[ui_netSource.integer]);
			break;
		case UI_NETFILTER:
			if(ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters)
			{
				ui_serverFilterType.integer = 0;
			}
			s = va("Filter: %s", serverFilters[ui_serverFilterType.integer].description);
			break;
		case UI_TIER:
			break;
		case UI_TIER_MAPNAME:
			break;
		case UI_TIER_GAMETYPE:
			break;
		case UI_ALLMAPS_SELECTION:
			break;
		case UI_OPPONENT_NAME:
			break;
		case UI_KEYBINDSTATUS:
			if(Display_KeyBindPending())
			{
				s = "Waiting for new key... Press ESCAPE to cancel";
			}
			else
			{
				s = "Press ENTER or CLICK to change, Press BACKSPACE to clear";
			}
			break;
		case UI_SERVERREFRESHDATE:
			s = UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer));
			break;
		default:
			break;
	}

	if(s)
	{
		return Text_Width(s, scale, 0);
	}
	return 0;
}

static void UI_DrawBotName(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	int             value = uiInfo.botIndex;
	const char     *text = "";

	if(value >= UI_GetNumBots())
		value = 0;

	text = UI_GetBotNameByNumber(value);

	Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle);
}

static void UI_DrawBotSkill(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(uiInfo.skillIndex >= 0 && uiInfo.skillIndex < numSkillLevels)
	{
		Text_Paint(rect->x, rect->y, scale, color, skillLevels[uiInfo.skillIndex], 0, 0, textStyle);
	}
}

static void UI_DrawRedBlue(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	Text_Paint(rect->x, rect->y, scale, color, (uiInfo.redBlue == 0) ? "Red" : "Blue", 0, 0, textStyle);
}

/*
===============
UI_BuildPlayerList
===============
*/
static void UI_BuildPlayerList(void)
{
	uiClientState_t cs;
	int             n, count, team, team2, playerTeamNumber;
	char            info[MAX_INFO_STRING];

	trap_GetClientState(&cs);
	trap_GetConfigString(CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING);
	uiInfo.playerNumber = cs.clientNum;
	uiInfo.teamLeader = atoi(Info_ValueForKey(info, "tl"));
	team = atoi(Info_ValueForKey(info, "t"));
	trap_GetConfigString(CS_SERVERINFO, info, sizeof(info));
	count = atoi(Info_ValueForKey(info, "sv_maxclients"));
	uiInfo.playerCount = 0;
	uiInfo.myTeamCount = 0;
	playerTeamNumber = 0;
	for(n = 0; n < count; n++)
	{
		trap_GetConfigString(CS_PLAYERS + n, info, MAX_INFO_STRING);

		if(info[0])
		{
			Q_strncpyz(uiInfo.playerNames[uiInfo.playerCount], Info_ValueForKey(info, "n"), MAX_NAME_LENGTH);
			Q_CleanStr(uiInfo.playerNames[uiInfo.playerCount]);
			uiInfo.playerCount++;
			team2 = atoi(Info_ValueForKey(info, "t"));
			if(team2 == team)
			{
				Q_strncpyz(uiInfo.teamNames[uiInfo.myTeamCount], Info_ValueForKey(info, "n"), MAX_NAME_LENGTH);
				Q_CleanStr(uiInfo.teamNames[uiInfo.myTeamCount]);
				uiInfo.teamClientNums[uiInfo.myTeamCount] = n;
				if(uiInfo.playerNumber == n)
				{
					playerTeamNumber = uiInfo.myTeamCount;
				}
				uiInfo.myTeamCount++;
			}
		}
	}

	if(!uiInfo.teamLeader)
	{
		trap_Cvar_Set("cg_selectedPlayer", va("%d", playerTeamNumber));
	}

	n = trap_Cvar_VariableValue("cg_selectedPlayer");
	if(n < 0 || n > uiInfo.myTeamCount)
	{
		n = 0;
	}
	if(n < uiInfo.myTeamCount)
	{
		trap_Cvar_Set("cg_selectedPlayerName", uiInfo.teamNames[n]);
	}
}


static void UI_DrawSelectedPlayer(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(uiInfo.uiDC.realTime > uiInfo.playerRefresh)
	{
		uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
		UI_BuildPlayerList();
	}
	Text_Paint(rect->x, rect->y, scale, color,
			   (uiInfo.teamLeader) ? UI_Cvar_VariableString("cg_selectedPlayerName") : UI_Cvar_VariableString("name"), 0, 0,
			   textStyle);
}

static void UI_DrawServerRefreshDate(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	if(uiInfo.serverStatus.refreshActive)
	{
		vec4_t          lowLight, newColor;

		lowLight[0] = 0.8 * color[0];
		lowLight[1] = 0.8 * color[1];
		lowLight[2] = 0.8 * color[2];
		lowLight[3] = 0.8 * color[3];
		LerpColor(color, lowLight, newColor, 0.5 + 0.5 * sin(uiInfo.uiDC.realTime / PULSE_DIVISOR));
		Text_Paint(rect->x, rect->y, scale, newColor,
				   va("Getting info for %d servers (ESC to cancel)", trap_LAN_GetServerCount(ui_netSource.integer)), 0, 0,
				   textStyle);
	}
	else
	{
		char            buff[64];

		Q_strncpyz(buff, UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)), 64);
		Text_Paint(rect->x, rect->y, scale, color, va("Refresh Time: %s", buff), 0, 0, textStyle);
	}
}

static void UI_DrawServerMOTD(rectDef_t * rect, float scale, vec4_t color)
{
	if(uiInfo.serverStatus.motdLen)
	{
		float           maxX;

		if(uiInfo.serverStatus.motdWidth == -1)
		{
			uiInfo.serverStatus.motdWidth = 0;
			uiInfo.serverStatus.motdPaintX = rect->x + 1;
			uiInfo.serverStatus.motdPaintX2 = -1;
		}

		if(uiInfo.serverStatus.motdOffset > uiInfo.serverStatus.motdLen)
		{
			uiInfo.serverStatus.motdOffset = 0;
			uiInfo.serverStatus.motdPaintX = rect->x + 1;
			uiInfo.serverStatus.motdPaintX2 = -1;
		}

		if(uiInfo.uiDC.realTime > uiInfo.serverStatus.motdTime)
		{
			uiInfo.serverStatus.motdTime = uiInfo.uiDC.realTime + 10;
			if(uiInfo.serverStatus.motdPaintX <= rect->x + 2)
			{
				if(uiInfo.serverStatus.motdOffset < uiInfo.serverStatus.motdLen)
				{
					uiInfo.serverStatus.motdPaintX +=
						Text_Width(&uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], scale, 1) - 1;
					uiInfo.serverStatus.motdOffset++;
				}
				else
				{
					uiInfo.serverStatus.motdOffset = 0;
					if(uiInfo.serverStatus.motdPaintX2 >= 0)
					{
						uiInfo.serverStatus.motdPaintX = uiInfo.serverStatus.motdPaintX2;
					}
					else
					{
						uiInfo.serverStatus.motdPaintX = rect->x + rect->w - 2;
					}
					uiInfo.serverStatus.motdPaintX2 = -1;
				}
			}
			else
			{
				//serverStatus.motdPaintX--;
				uiInfo.serverStatus.motdPaintX -= 2;
				if(uiInfo.serverStatus.motdPaintX2 >= 0)
				{
					//serverStatus.motdPaintX2--;
					uiInfo.serverStatus.motdPaintX2 -= 2;
				}
			}
		}

		maxX = rect->x + rect->w - 2;
		Text_Paint_Limit(&maxX, uiInfo.serverStatus.motdPaintX, rect->y + rect->h - 3, scale, color,
						 &uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], 0, 0);
		if(uiInfo.serverStatus.motdPaintX2 >= 0)
		{
			float           maxX2 = rect->x + rect->w - 2;

			Text_Paint_Limit(&maxX2, uiInfo.serverStatus.motdPaintX2, rect->y + rect->h - 3, scale, color,
							 uiInfo.serverStatus.motd, 0, uiInfo.serverStatus.motdOffset);
		}
		if(uiInfo.serverStatus.motdOffset && maxX > 0)
		{
			// if we have an offset ( we are skipping the first part of the string ) and we fit the string
			if(uiInfo.serverStatus.motdPaintX2 == -1)
			{
				uiInfo.serverStatus.motdPaintX2 = rect->x + rect->w - 2;
			}
		}
		else
		{
			uiInfo.serverStatus.motdPaintX2 = -1;
		}

	}
}

static void UI_DrawKeyBindStatus(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
//  int ofs = 0; TTimo: unused
	if(Display_KeyBindPending())
	{
		Text_Paint(rect->x, rect->y, scale, color, "Waiting for new key... Press ESCAPE to cancel", 0, 0, textStyle);
	}
	else
	{
		Text_Paint(rect->x, rect->y, scale, color, "Press ENTER or CLICK to change, Press BACKSPACE to clear", 0, 0, textStyle);
	}
}

static void UI_DrawGLInfo(rectDef_t * rect, float scale, vec4_t color, int textStyle)
{
	char           *eptr;
	char            buff[1024];
	const char     *lines[64];
	int             y, numLines, i;

	Text_Paint(rect->x + 2, rect->y, scale, color, va("VENDOR: %s", uiInfo.uiDC.glconfig.vendor_string), 0, 30, textStyle);
	Text_Paint(rect->x + 2, rect->y + 15, scale, color,
			   va("VERSION: %s: %s", uiInfo.uiDC.glconfig.version_string, uiInfo.uiDC.glconfig.renderer_string), 0, 30,
			   textStyle);
	Text_Paint(rect->x + 2, rect->y + 30, scale, color,
			   va("PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)", uiInfo.uiDC.glconfig.colorBits,
				  uiInfo.uiDC.glconfig.depthBits, uiInfo.uiDC.glconfig.stencilBits), 0, 30, textStyle);

	// build null terminated extension strings
	// TTimo: https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399
	// in TA this was not directly crashing, but displaying a nasty broken shader right in the middle
	// brought down the string size to 1024, there's not much that can be shown on the screen anyway
	Q_strncpyz(buff, uiInfo.uiDC.glconfig.extensions_string, 1024);
	eptr = buff;
	y = rect->y + 45;
	numLines = 0;
	while(y < rect->y + rect->h && *eptr)
	{
		while(*eptr && *eptr == ' ')
			*eptr++ = '\0';

		// track start of valid string
		if(*eptr && *eptr != ' ')
		{
			lines[numLines++] = eptr;
		}

		while(*eptr && *eptr != ' ')
			eptr++;
	}

	i = 0;
	while(i < numLines)
	{
		Text_Paint(rect->x + 2, y, scale, color, lines[i++], 0, 20, textStyle);
		if(i < numLines)
		{
			Text_Paint(rect->x + rect->w / 2, y, scale, color, lines[i++], 0, 20, textStyle);
		}
		y += 10;
		if(y > rect->y + rect->h - 11)
		{
			break;
		}
	}


}

// FIXME: table drive
//
static void UI_OwnerDraw(float x, float y, float w, float h,
						 float text_x, float text_y, int ownerDraw,
						 int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle)
{
	rectDef_t       rect;
	tremInfoPane_t *pane = NULL;

	rect.x = x + text_x;
	rect.y = y + text_y;
	rect.w = w;
	rect.h = h;

	switch (ownerDraw)
	{
		case UI_TEAMINFOPANE:
			if((pane = uiInfo.tremTeamList[uiInfo.tremTeamIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_ACLASSINFOPANE:
			if((pane = uiInfo.tremAlienClassList[uiInfo.tremAlienClassIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_AUPGRADEINFOPANE:
			if((pane = uiInfo.tremAlienUpgradeList[uiInfo.tremAlienUpgradeIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_HITEMINFOPANE:
			if((pane = uiInfo.tremHumanItemList[uiInfo.tremHumanItemIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_HBUYINFOPANE:
			if((pane = uiInfo.tremHumanArmouryBuyList[uiInfo.tremHumanArmouryBuyIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_HSELLINFOPANE:
			if((pane = uiInfo.tremHumanArmourySellList[uiInfo.tremHumanArmourySellIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_ABUILDINFOPANE:
			if((pane = uiInfo.tremAlienBuildList[uiInfo.tremAlienBuildIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_HBUILDINFOPANE:
			if((pane = uiInfo.tremHumanBuildList[uiInfo.tremHumanBuildIndex].infopane))
				UI_DrawInfoPane(pane, &rect, text_x, text_y, scale, color, textStyle);
			break;

		case UI_HANDICAP:
			UI_DrawHandicap(&rect, scale, color, textStyle);
			break;
		case UI_PLAYERMODEL:
			UI_DrawPlayerModel(&rect);
			break;
		case UI_CLANNAME:
			UI_DrawClanName(&rect, scale, color, textStyle);
			break;
		case UI_CLANLOGO:
			UI_DrawClanLogo(&rect, scale, color);
			break;
		case UI_CLANCINEMATIC:
			UI_DrawClanCinematic(&rect, scale, color);
			break;
		case UI_PREVIEWCINEMATIC:
			UI_DrawPreviewCinematic(&rect, scale, color);
			break;
		case UI_GAMETYPE:
			UI_DrawGameType(&rect, scale, color, textStyle);
			break;
		case UI_NETGAMETYPE:
			UI_DrawNetGameType(&rect, scale, color, textStyle);
			break;
		case UI_JOINGAMETYPE:
			UI_DrawJoinGameType(&rect, scale, color, textStyle);
			break;
		case UI_MAPPREVIEW:
			UI_DrawMapPreview(&rect, scale, color, qtrue);
			break;
		case UI_MAP_TIMETOBEAT:
			UI_DrawMapTimeToBeat(&rect, scale, color, textStyle);
			break;
		case UI_MAPCINEMATIC:
			UI_DrawMapCinematic(&rect, scale, color, qfalse);
			break;
		case UI_STARTMAPCINEMATIC:
			UI_DrawMapCinematic(&rect, scale, color, qtrue);
			break;
		case UI_SKILL:
			UI_DrawSkill(&rect, scale, color, textStyle);
			break;
		case UI_BLUETEAMNAME:
			UI_DrawTeamName(&rect, scale, color, qtrue, textStyle);
			break;
		case UI_REDTEAMNAME:
			UI_DrawTeamName(&rect, scale, color, qfalse, textStyle);
			break;
		case UI_BLUETEAM1:
		case UI_BLUETEAM2:
		case UI_BLUETEAM3:
		case UI_BLUETEAM4:
		case UI_BLUETEAM5:
			UI_DrawTeamMember(&rect, scale, color, qtrue, ownerDraw - UI_BLUETEAM1 + 1, textStyle);
			break;
		case UI_REDTEAM1:
		case UI_REDTEAM2:
		case UI_REDTEAM3:
		case UI_REDTEAM4:
		case UI_REDTEAM5:
			UI_DrawTeamMember(&rect, scale, color, qfalse, ownerDraw - UI_REDTEAM1 + 1, textStyle);
			break;
		case UI_NETSOURCE:
			UI_DrawNetSource(&rect, scale, color, textStyle);
			break;
		case UI_NETMAPPREVIEW:
			UI_DrawNetMapPreview(&rect, scale, color);
			break;
		case UI_NETMAPCINEMATIC:
			UI_DrawNetMapCinematic(&rect, scale, color);
			break;
		case UI_NETFILTER:
			UI_DrawNetFilter(&rect, scale, color, textStyle);
			break;
		case UI_TIER:
			UI_DrawTier(&rect, scale, color, textStyle);
			break;
		case UI_OPPONENTMODEL:
			UI_DrawOpponent(&rect);
			break;
		case UI_TIERMAP1:
			UI_DrawTierMap(&rect, 0);
			break;
		case UI_TIERMAP2:
			UI_DrawTierMap(&rect, 1);
			break;
		case UI_TIERMAP3:
			UI_DrawTierMap(&rect, 2);
			break;
		case UI_PLAYERLOGO:
			UI_DrawPlayerLogo(&rect, color);
			break;
		case UI_PLAYERLOGO_METAL:
			UI_DrawPlayerLogoMetal(&rect, color);
			break;
		case UI_PLAYERLOGO_NAME:
			UI_DrawPlayerLogoName(&rect, color);
			break;
		case UI_OPPONENTLOGO:
			UI_DrawOpponentLogo(&rect, color);
			break;
		case UI_OPPONENTLOGO_METAL:
			UI_DrawOpponentLogoMetal(&rect, color);
			break;
		case UI_OPPONENTLOGO_NAME:
			UI_DrawOpponentLogoName(&rect, color);
			break;
		case UI_TIER_MAPNAME:
			UI_DrawTierMapName(&rect, scale, color, textStyle);
			break;
		case UI_TIER_GAMETYPE:
			UI_DrawTierGameType(&rect, scale, color, textStyle);
			break;
		case UI_ALLMAPS_SELECTION:
			UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qtrue);
			break;
		case UI_MAPS_SELECTION:
			UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qfalse);
			break;
		case UI_OPPONENT_NAME:
			UI_DrawOpponentName(&rect, scale, color, textStyle);
			break;
		case UI_BOTNAME:
			UI_DrawBotName(&rect, scale, color, textStyle);
			break;
		case UI_BOTSKILL:
			UI_DrawBotSkill(&rect, scale, color, textStyle);
			break;
		case UI_REDBLUE:
			UI_DrawRedBlue(&rect, scale, color, textStyle);
			break;
		case UI_SELECTEDPLAYER:
			UI_DrawSelectedPlayer(&rect, scale, color, textStyle);
			break;
		case UI_SERVERREFRESHDATE:
			UI_DrawServerRefreshDate(&rect, scale, color, textStyle);
			break;
		case UI_SERVERMOTD:
			UI_DrawServerMOTD(&rect, scale, color);
			break;
		case UI_GLINFO:
			UI_DrawGLInfo(&rect, scale, color, textStyle);
			break;
		case UI_KEYBINDSTATUS:
			UI_DrawKeyBindStatus(&rect, scale, color, textStyle);
			break;
		default:
			break;
	}

}

static qboolean UI_OwnerDrawVisible(int flags)
{
	qboolean        vis = qtrue;
	uiClientState_t cs;
	pTeam_t         team;
	char            info[MAX_INFO_STRING];

	trap_GetClientState(&cs);
	trap_GetConfigString(CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING);
	team = atoi(Info_ValueForKey(info, "t"));


	while(flags)
	{

		if(flags & UI_SHOW_NOTSPECTATING)
		{
			if(team == PTE_NONE)
				vis = qfalse;

			flags &= ~UI_SHOW_NOTSPECTATING;
		}

		if(flags & UI_SHOW_VOTEACTIVE)
		{
			if(!trap_Cvar_VariableValue("ui_voteActive"))
				vis = qfalse;

			flags &= ~UI_SHOW_VOTEACTIVE;
		}

		if(flags & UI_SHOW_CANVOTE)
		{
			if(trap_Cvar_VariableValue("ui_voteActive"))
				vis = qfalse;

			flags &= ~UI_SHOW_CANVOTE;
		}

		if(flags & UI_SHOW_TEAMVOTEACTIVE)
		{
			if(team == PTE_ALIENS)
			{
				if(!trap_Cvar_VariableValue("ui_alienTeamVoteActive"))
					vis = qfalse;
			}
			else if(team == PTE_HUMANS)
			{
				if(!trap_Cvar_VariableValue("ui_humanTeamVoteActive"))
					vis = qfalse;
			}

			flags &= ~UI_SHOW_TEAMVOTEACTIVE;
		}

		if(flags & UI_SHOW_CANTEAMVOTE)
		{
			if(team == PTE_ALIENS)
			{
				if(trap_Cvar_VariableValue("ui_alienTeamVoteActive"))
					vis = qfalse;
			}
			else if(team == PTE_HUMANS)
			{
				if(trap_Cvar_VariableValue("ui_humanTeamVoteActive"))
					vis = qfalse;
			}

			flags &= ~UI_SHOW_CANTEAMVOTE;
		}

		if(flags & UI_SHOW_LEADER)
		{
			// these need to show when this client can give orders to a player or a group
			if(!uiInfo.teamLeader)
			{
				vis = qfalse;
			}
			else
			{
				// if showing yourself
				if(ui_selectedPlayer.integer < uiInfo.myTeamCount &&
				   uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber)
				{
					vis = qfalse;
				}
			}
			flags &= ~UI_SHOW_LEADER;
		}
		if(flags & UI_SHOW_NOTLEADER)
		{
			// these need to show when this client is assigning their own status or they are NOT the leader
			if(uiInfo.teamLeader)
			{
				// if not showing yourself
				if(!
				   (ui_selectedPlayer.integer < uiInfo.myTeamCount &&
					uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber))
				{
					vis = qfalse;
				}
				// these need to show when this client can give orders to a player or a group
			}
			flags &= ~UI_SHOW_NOTLEADER;
		}
		if(flags & UI_SHOW_FAVORITESERVERS)
		{
			// this assumes you only put this type of display flag on something showing in the proper context
			if(ui_netSource.integer != AS_FAVORITES)
			{
				vis = qfalse;
			}
			flags &= ~UI_SHOW_FAVORITESERVERS;
		}
		if(flags & UI_SHOW_NOTFAVORITESERVERS)
		{
			// this assumes you only put this type of display flag on something showing in the proper context
			if(ui_netSource.integer == AS_FAVORITES)
			{
				vis = qfalse;
			}
			flags &= ~UI_SHOW_NOTFAVORITESERVERS;
		}
		if(flags & UI_SHOW_NEWHIGHSCORE)
		{
			if(uiInfo.newHighScoreTime < uiInfo.uiDC.realTime)
			{
				vis = qfalse;
			}
			else
			{
				if(uiInfo.soundHighScore)
				{
					if(trap_Cvar_VariableValue("sv_killserver") == 0)
					{
						// wait on server to go down before playing sound
						trap_S_StartLocalSound(uiInfo.newHighScoreSound, CHAN_ANNOUNCER);
						uiInfo.soundHighScore = qfalse;
					}
				}
			}
			flags &= ~UI_SHOW_NEWHIGHSCORE;
		}
		if(flags & UI_SHOW_NEWBESTTIME)
		{
			if(uiInfo.newBestTime < uiInfo.uiDC.realTime)
			{
				vis = qfalse;
			}
			flags &= ~UI_SHOW_NEWBESTTIME;
		}
		if(flags & UI_SHOW_DEMOAVAILABLE)
		{
			if(!uiInfo.demoAvailable)
			{
				vis = qfalse;
			}
			flags &= ~UI_SHOW_DEMOAVAILABLE;
		}
		else
		{
			flags = 0;
		}
	}
	return vis;
}

static qboolean UI_Handicap_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             h;

		h = Com_Clamp(5, 100, trap_Cvar_VariableValue("handicap"));
		if(key == K_MOUSE2)
		{
			h -= 5;
		}
		else
		{
			h += 5;
		}
		if(h > 100)
		{
			h = 5;
		}
		else if(h < 0)
		{
			h = 100;
		}
		trap_Cvar_Set("handicap", va("%i", h));
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_ClanName_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             i;

		i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
		if(uiInfo.teamList[i].cinematic >= 0)
		{
			trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic);
			uiInfo.teamList[i].cinematic = -1;
		}
		if(key == K_MOUSE2)
		{
			i--;
		}
		else
		{
			i++;
		}
		if(i >= uiInfo.teamCount)
		{
			i = 0;
		}
		else if(i < 0)
		{
			i = uiInfo.teamCount - 1;
		}
		trap_Cvar_Set("ui_teamName", uiInfo.teamList[i].teamName);
		UI_HeadCountByTeam();
		UI_FeederSelection(FEEDER_HEADS, 0);
		updateModel = qtrue;
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_GameType_HandleKey(int flags, float *special, int key, qboolean resetMap)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             oldCount = UI_MapCountByGameType(qtrue);

		// hard coded mess here
		if(key == K_MOUSE2)
		{
			ui_gameType.integer--;
			if(ui_gameType.integer == 2)
			{
				ui_gameType.integer = 1;
			}
			else if(ui_gameType.integer < 2)
			{
				ui_gameType.integer = uiInfo.numGameTypes - 1;
			}
		}
		else
		{
			ui_gameType.integer++;
			if(ui_gameType.integer >= uiInfo.numGameTypes)
			{
				ui_gameType.integer = 1;
			}
			else if(ui_gameType.integer == 2)
			{
				ui_gameType.integer = 3;
			}
		}

		trap_Cvar_Set("ui_Q3Model", "0");

		trap_Cvar_Set("ui_gameType", va("%d", ui_gameType.integer));
		UI_SetCapFragLimits(qtrue);
		UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);
		if(resetMap && oldCount != UI_MapCountByGameType(qtrue))
		{
			trap_Cvar_Set("ui_currentMap", "0");
			Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, NULL);
		}
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_NetGameType_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{

		if(key == K_MOUSE2)
		{
			ui_netGameType.integer--;
		}
		else
		{
			ui_netGameType.integer++;
		}

		if(ui_netGameType.integer < 0)
		{
			ui_netGameType.integer = uiInfo.numGameTypes - 1;
		}
		else if(ui_netGameType.integer >= uiInfo.numGameTypes)
		{
			ui_netGameType.integer = 0;
		}

		trap_Cvar_Set("ui_netGameType", va("%d", ui_netGameType.integer));
		trap_Cvar_Set("ui_actualnetGameType", va("%d", uiInfo.gameTypes[ui_netGameType.integer].gtEnum));
		trap_Cvar_Set("ui_currentNetMap", "0");
		UI_MapCountByGameType(qfalse);
		Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, NULL);
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_JoinGameType_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{

		if(key == K_MOUSE2)
		{
			ui_joinGameType.integer--;
		}
		else
		{
			ui_joinGameType.integer++;
		}

		if(ui_joinGameType.integer < 0)
		{
			ui_joinGameType.integer = uiInfo.numJoinGameTypes - 1;
		}
		else if(ui_joinGameType.integer >= uiInfo.numJoinGameTypes)
		{
			ui_joinGameType.integer = 0;
		}

		trap_Cvar_Set("ui_joinGameType", va("%d", ui_joinGameType.integer));
		UI_BuildServerDisplayList(qtrue);
		return qtrue;
	}
	return qfalse;
}



static qboolean UI_Skill_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             i = trap_Cvar_VariableValue("g_spSkill");

		if(key == K_MOUSE2)
		{
			i--;
		}
		else
		{
			i++;
		}

		if(i < 1)
		{
			i = numSkillLevels;
		}
		else if(i > numSkillLevels)
		{
			i = 1;
		}

		trap_Cvar_Set("g_spSkill", va("%i", i));
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_TeamName_HandleKey(int flags, float *special, int key, qboolean blue)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             i;

		i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam"));

		if(key == K_MOUSE2)
		{
			i--;
		}
		else
		{
			i++;
		}

		if(i >= uiInfo.teamCount)
		{
			i = 0;
		}
		else if(i < 0)
		{
			i = uiInfo.teamCount - 1;
		}

		trap_Cvar_Set((blue) ? "ui_blueTeam" : "ui_redTeam", uiInfo.teamList[i].teamName);

		return qtrue;
	}
	return qfalse;
}

static qboolean UI_TeamMember_HandleKey(int flags, float *special, int key, qboolean blue, int num)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		// 0 - None
		// 1 - Human
		// 2..NumCharacters - Bot
		char           *cvar = va(blue ? "ui_blueteam%i" : "ui_redteam%i", num);
		int             value = trap_Cvar_VariableValue(cvar);

		if(key == K_MOUSE2)
		{
			value--;
		}
		else
		{
			value++;
		}

		if(value >= UI_GetNumBots() + 2)
			value = 0;
		else if(value < 0)
			value = UI_GetNumBots() + 2 - 1;

		trap_Cvar_Set(cvar, va("%i", value));
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_NetSource_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{

		if(key == K_MOUSE2)
		{
			ui_netSource.integer--;
			if(ui_netSource.integer == AS_MPLAYER)
				ui_netSource.integer--;
		}
		else
		{
			ui_netSource.integer++;
			if(ui_netSource.integer == AS_MPLAYER)
				ui_netSource.integer++;
		}

		if(ui_netSource.integer >= numNetSources)
		{
			ui_netSource.integer = 0;
		}
		else if(ui_netSource.integer < 0)
		{
			ui_netSource.integer = numNetSources - 1;
		}

		UI_BuildServerDisplayList(qtrue);
		if(ui_netSource.integer != AS_GLOBAL)
		{
			UI_StartServerRefresh(qtrue);
		}
		trap_Cvar_Set("ui_netSource", va("%d", ui_netSource.integer));
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_NetFilter_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{

		if(key == K_MOUSE2)
		{
			ui_serverFilterType.integer--;
		}
		else
		{
			ui_serverFilterType.integer++;
		}

		if(ui_serverFilterType.integer >= numServerFilters)
		{
			ui_serverFilterType.integer = 0;
		}
		else if(ui_serverFilterType.integer < 0)
		{
			ui_serverFilterType.integer = numServerFilters - 1;
		}
		UI_BuildServerDisplayList(qtrue);
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_OpponentName_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		if(key == K_MOUSE2)
		{
			UI_PriorOpponent();
		}
		else
		{
			UI_NextOpponent();
		}
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_BotName_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             value = uiInfo.botIndex;

		if(key == K_MOUSE2)
		{
			value--;
		}
		else
		{
			value++;
		}


		if(value >= UI_GetNumBots() + 2)
			value = 0;
		else if(value < 0)
			value = UI_GetNumBots() + 2 - 1;

		uiInfo.botIndex = value;
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_BotSkill_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		if(key == K_MOUSE2)
		{
			uiInfo.skillIndex--;
		}
		else
		{
			uiInfo.skillIndex++;
		}
		if(uiInfo.skillIndex >= numSkillLevels)
		{
			uiInfo.skillIndex = 0;
		}
		else if(uiInfo.skillIndex < 0)
		{
			uiInfo.skillIndex = numSkillLevels - 1;
		}
		return qtrue;
	}
	return qfalse;
}

static qboolean UI_RedBlue_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		uiInfo.redBlue ^= 1;
		return qtrue;
	}
	return qfalse;
}



static qboolean UI_SelectedPlayer_HandleKey(int flags, float *special, int key)
{
	if(key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER)
	{
		int             selected;

		UI_BuildPlayerList();
		if(!uiInfo.teamLeader)
		{
			return qfalse;
		}
		selected = trap_Cvar_VariableValue("cg_selectedPlayer");

		if(key == K_MOUSE2)
		{
			selected--;
		}
		else
		{
			selected++;
		}

		if(selected > uiInfo.myTeamCount)
		{
			selected = 0;
		}
		else if(selected < 0)
		{
			selected = uiInfo.myTeamCount;
		}

		if(selected == uiInfo.myTeamCount)
		{
			trap_Cvar_Set("cg_selectedPlayerName", "Everyone");
		}
		else
		{
			trap_Cvar_Set("cg_selectedPlayerName", uiInfo.teamNames[selected]);
		}
		trap_Cvar_Set("cg_selectedPlayer", va("%d", selected));
	}
	return qfalse;
}


static qboolean UI_OwnerDrawHandleKey(int ownerDraw, int flags, float *special, int key)
{
	switch (ownerDraw)
	{
		case UI_HANDICAP:
			return UI_Handicap_HandleKey(flags, special, key);
			break;
		case UI_CLANNAME:
			return UI_ClanName_HandleKey(flags, special, key);
			break;
		case UI_GAMETYPE:
			return UI_GameType_HandleKey(flags, special, key, qtrue);
			break;
		case UI_NETGAMETYPE:
			return UI_NetGameType_HandleKey(flags, special, key);
			break;
		case UI_JOINGAMETYPE:
			return UI_JoinGameType_HandleKey(flags, special, key);
			break;
		case UI_SKILL:
			return UI_Skill_HandleKey(flags, special, key);
			break;
		case UI_BLUETEAMNAME:
			return UI_TeamName_HandleKey(flags, special, key, qtrue);
			break;
		case UI_REDTEAMNAME:
			return UI_TeamName_HandleKey(flags, special, key, qfalse);
			break;
		case UI_BLUETEAM1:
		case UI_BLUETEAM2:
		case UI_BLUETEAM3:
		case UI_BLUETEAM4:
		case UI_BLUETEAM5:
			UI_TeamMember_HandleKey(flags, special, key, qtrue, ownerDraw - UI_BLUETEAM1 + 1);
			break;
		case UI_REDTEAM1:
		case UI_REDTEAM2:
		case UI_REDTEAM3:
		case UI_REDTEAM4:
		case UI_REDTEAM5:
			UI_TeamMember_HandleKey(flags, special, key, qfalse, ownerDraw - UI_REDTEAM1 + 1);
			break;
		case UI_NETSOURCE:
			UI_NetSource_HandleKey(flags, special, key);
			break;
		case UI_NETFILTER:
			UI_NetFilter_HandleKey(flags, special, key);
			break;
		case UI_OPPONENT_NAME:
			UI_OpponentName_HandleKey(flags, special, key);
			break;
		case UI_BOTNAME:
			return UI_BotName_HandleKey(flags, special, key);
			break;
		case UI_BOTSKILL:
			return UI_BotSkill_HandleKey(flags, special, key);
			break;
		case UI_REDBLUE:
			UI_RedBlue_HandleKey(flags, special, key);
			break;
		case UI_SELECTEDPLAYER:
			UI_SelectedPlayer_HandleKey(flags, special, key);
			break;
		default:
			break;
	}

	return qfalse;
}


static float UI_GetValue(int ownerDraw)
{
	return 0;
}

/*
=================
UI_ServersQsortCompare
=================
*/
static int QDECL UI_ServersQsortCompare(const void *arg1, const void *arg2)
{
	return trap_LAN_CompareServers(ui_netSource.integer, uiInfo.serverStatus.sortKey, uiInfo.serverStatus.sortDir, *(int *)arg1,
								   *(int *)arg2);
}


/*
=================
UI_ServersSort
=================
*/
void UI_ServersSort(int column, qboolean force)
{

	if(!force)
	{
		if(uiInfo.serverStatus.sortKey == column)
		{
			return;
		}
	}

	uiInfo.serverStatus.sortKey = column;
	qsort(&uiInfo.serverStatus.displayServers[0], uiInfo.serverStatus.numDisplayServers, sizeof(int), UI_ServersQsortCompare);
}


/*
===============
UI_GetCurrentAlienStage
===============
*/
static stage_t UI_GetCurrentAlienStage(void)
{
	char            buffer[MAX_TOKEN_CHARS];
	stage_t         stage, dummy;

	trap_Cvar_VariableStringBuffer("ui_stages", buffer, sizeof(buffer));
	sscanf(buffer, "%d %d", (int *)&stage, (int *)&dummy);

	return stage;
}

/*
===============
UI_GetCurrentHumanStage
===============
*/
static stage_t UI_GetCurrentHumanStage(void)
{
	char            buffer[MAX_TOKEN_CHARS];
	stage_t         stage, dummy;

	trap_Cvar_VariableStringBuffer("ui_stages", buffer, sizeof(buffer));
	sscanf(buffer, "%d %d", (int *)&dummy, (int *)&stage);

	return stage;
}

/*
===============
UI_LoadTremTeams
===============
*/
static void UI_LoadTremTeams(void)
{
	uiInfo.tremTeamCount = 4;

	uiInfo.tremTeamList[0].text = String_Alloc("Aliens");
	uiInfo.tremTeamList[0].cmd = String_Alloc("cmd team aliens\n");
	uiInfo.tremTeamList[0].infopane = UI_FindInfoPaneByName("alienteam");

	uiInfo.tremTeamList[1].text = String_Alloc("Humans");
	uiInfo.tremTeamList[1].cmd = String_Alloc("cmd team humans\n");
	uiInfo.tremTeamList[1].infopane = UI_FindInfoPaneByName("humanteam");

	uiInfo.tremTeamList[2].text = String_Alloc("Spectate");
	uiInfo.tremTeamList[2].cmd = String_Alloc("cmd team spectate\n");
	uiInfo.tremTeamList[2].infopane = UI_FindInfoPaneByName("spectateteam");

	uiInfo.tremTeamList[3].text = String_Alloc("Auto select");
	uiInfo.tremTeamList[3].cmd = String_Alloc("cmd team auto\n");
	uiInfo.tremTeamList[3].infopane = UI_FindInfoPaneByName("autoteam");
}

/*
===============
UI_AddClass
===============
*/
static void UI_AddClass(pClass_t class)
{
	uiInfo.tremAlienClassList[uiInfo.tremAlienClassCount].text = String_Alloc(BG_FindHumanNameForClassNum(class));
	uiInfo.tremAlienClassList[uiInfo.tremAlienClassCount].cmd = String_Alloc(va("cmd class %s\n", BG_FindNameForClassNum(class)));
	uiInfo.tremAlienClassList[uiInfo.tremAlienClassCount].infopane =
		UI_FindInfoPaneByName(va("%sclass", BG_FindNameForClassNum(class)));

	uiInfo.tremAlienClassCount++;
}

/*
===============
UI_LoadTremAlienClasses
===============
*/
static void UI_LoadTremAlienClasses(void)
{
	uiInfo.tremAlienClassCount = 0;

	if(BG_ClassIsAllowed(PCL_ALIEN_LEVEL0))
		UI_AddClass(PCL_ALIEN_LEVEL0);

	if(BG_ClassIsAllowed(PCL_ALIEN_BUILDER0_UPG) && BG_FindStagesForClass(PCL_ALIEN_BUILDER0_UPG, UI_GetCurrentAlienStage()))
		UI_AddClass(PCL_ALIEN_BUILDER0_UPG);
	else if(BG_ClassIsAllowed(PCL_ALIEN_BUILDER0))
		UI_AddClass(PCL_ALIEN_BUILDER0);
}

/*
===============
UI_AddItem
===============
*/
static void UI_AddItem(weapon_t weapon)
{
	uiInfo.tremHumanItemList[uiInfo.tremHumanItemCount].text = String_Alloc(BG_FindHumanNameForWeapon(weapon));
	uiInfo.tremHumanItemList[uiInfo.tremHumanItemCount].cmd = String_Alloc(va("cmd class %s\n", BG_FindNameForWeapon(weapon)));
	uiInfo.tremHumanItemList[uiInfo.tremHumanItemCount].infopane =
		UI_FindInfoPaneByName(va("%sitem", BG_FindNameForWeapon(weapon)));

	uiInfo.tremHumanItemCount++;
}

/*
===============
UI_LoadTremHumanItems
===============
*/
static void UI_LoadTremHumanItems(void)
{
	uiInfo.tremHumanItemCount = 0;

	if(BG_WeaponIsAllowed(WP_MACHINEGUN))
		UI_AddItem(WP_MACHINEGUN);

	if(BG_WeaponIsAllowed(WP_HBUILD2) && BG_FindStagesForWeapon(WP_HBUILD2, UI_GetCurrentHumanStage()))
		UI_AddItem(WP_HBUILD2);
	else if(BG_WeaponIsAllowed(WP_HBUILD))
		UI_AddItem(WP_HBUILD);
}

/*
===============
UI_ParseCarriageList
===============
*/
static void UI_ParseCarriageList(int *weapons, int *upgrades)
{
	int             i;
	char            carriageCvar[MAX_TOKEN_CHARS];
	char           *iterator;
	char            buffer[MAX_TOKEN_CHARS];
	char           *bufPointer;

	trap_Cvar_VariableStringBuffer("ui_carriage", carriageCvar, sizeof(carriageCvar));
	iterator = carriageCvar;

	if(weapons)
		*weapons = 0;

	if(upgrades)
		*upgrades = 0;

	//simple parser to give rise to weapon/upgrade list
	while(iterator && iterator[0] != '$')
	{
		bufPointer = buffer;

		if(iterator[0] == 'W')
		{
			iterator++;

			while(iterator[0] != ' ')
				*bufPointer++ = *iterator++;

			*bufPointer++ = '\n';

			i = atoi(buffer);

			if(weapons)
				*weapons |= (1 << i);
		}
		else if(iterator[0] == 'U')
		{
			iterator++;

			while(iterator[0] != ' ')
				*bufPointer++ = *iterator++;

			*bufPointer++ = '\n';

			i = atoi(buffer);

			if(upgrades)
				*upgrades |= (1 << i);
		}

		iterator++;
	}
}

/*
===============
UI_LoadTremHumanArmouryBuys
===============
*/
static void UI_LoadTremHumanArmouryBuys(void)
{
	int             i, j = 0;
	stage_t         stage = UI_GetCurrentHumanStage();
	int             weapons, upgrades;
	int             slots = 0;

	UI_ParseCarriageList(&weapons, &upgrades);

	for(i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++)
	{
		if(weapons & (1 << i))
			slots |= BG_FindSlotsForWeapon(i);
	}

	for(i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++)
	{
		if(upgrades & (1 << i))
			slots |= BG_FindSlotsForUpgrade(i);
	}

	uiInfo.tremHumanArmouryBuyCount = 0;

	for(i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++)
	{
		if(BG_FindTeamForWeapon(i) == WUT_HUMANS &&
		   BG_FindPurchasableForWeapon(i) &&
		   BG_FindStagesForWeapon(i, stage) &&
		   BG_WeaponIsAllowed(i) && !(BG_FindSlotsForWeapon(i) & slots) && !(weapons & (1 << i)))
		{
			uiInfo.tremHumanArmouryBuyList[j].text = String_Alloc(BG_FindHumanNameForWeapon(i));
			uiInfo.tremHumanArmouryBuyList[j].cmd = String_Alloc(va("cmd buy %s retrigger\n", BG_FindNameForWeapon(i)));
			uiInfo.tremHumanArmouryBuyList[j].infopane = UI_FindInfoPaneByName(va("%sitem", BG_FindNameForWeapon(i)));

			j++;

			uiInfo.tremHumanArmouryBuyCount++;
		}
	}

	for(i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++)
	{
		if(BG_FindTeamForUpgrade(i) == WUT_HUMANS &&
		   BG_FindPurchasableForUpgrade(i) &&
		   BG_FindStagesForUpgrade(i, stage) &&
		   BG_UpgradeIsAllowed(i) && !(BG_FindSlotsForUpgrade(i) & slots) && !(upgrades & (1 << i)))
		{
			uiInfo.tremHumanArmouryBuyList[j].text = String_Alloc(BG_FindHumanNameForUpgrade(i));
			uiInfo.tremHumanArmouryBuyList[j].cmd = String_Alloc(va("cmd buy %s retrigger\n", BG_FindNameForUpgrade(i)));
			uiInfo.tremHumanArmouryBuyList[j].infopane = UI_FindInfoPaneByName(va("%sitem", BG_FindNameForUpgrade(i)));

			j++;

			uiInfo.tremHumanArmouryBuyCount++;
		}
	}
}

/*
===============
UI_LoadTremHumanArmourySells
===============
*/
static void UI_LoadTremHumanArmourySells(void)
{
	int             weapons, upgrades;
	int             i, j = 0;

	uiInfo.tremHumanArmourySellCount = 0;
	UI_ParseCarriageList(&weapons, &upgrades);

	for(i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++)
	{
		if(weapons & (1 << i))
		{
			uiInfo.tremHumanArmourySellList[j].text = String_Alloc(BG_FindHumanNameForWeapon(i));
			uiInfo.tremHumanArmourySellList[j].cmd = String_Alloc(va("cmd sell %s retrigger\n", BG_FindNameForWeapon(i)));
			uiInfo.tremHumanArmourySellList[j].infopane = UI_FindInfoPaneByName(va("%sitem", BG_FindNameForWeapon(i)));

			j++;

			uiInfo.tremHumanArmourySellCount++;
		}
	}

	for(i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++)
	{
		if(upgrades & (1 << i))
		{
			uiInfo.tremHumanArmourySellList[j].text = String_Alloc(BG_FindHumanNameForUpgrade(i));
			uiInfo.tremHumanArmourySellList[j].cmd = String_Alloc(va("cmd sell %s retrigger\n", BG_FindNameForUpgrade(i)));
			uiInfo.tremHumanArmourySellList[j].infopane = UI_FindInfoPaneByName(va("%sitem", BG_FindNameForUpgrade(i)));

			j++;

			uiInfo.tremHumanArmourySellCount++;
		}
	}
}

/*
===============
UI_LoadTremAlienUpgrades
===============
*/
static void UI_LoadTremAlienUpgrades(void)
{
	int             i, j = 0;
	int             class, credits;
	char            ui_currentClass[MAX_STRING_CHARS];
	stage_t         stage = UI_GetCurrentAlienStage();

	trap_Cvar_VariableStringBuffer("ui_currentClass", ui_currentClass, MAX_STRING_CHARS);
	sscanf(ui_currentClass, "%d %d", &class, &credits);

	uiInfo.tremAlienUpgradeCount = 0;

	for(i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++)
	{
		if(BG_ClassCanEvolveFromTo(class, i, credits, 0) >= 0 && BG_FindStagesForClass(i, stage) && BG_ClassIsAllowed(i))
		{
			uiInfo.tremAlienUpgradeList[j].text = String_Alloc(BG_FindHumanNameForClassNum(i));
			uiInfo.tremAlienUpgradeList[j].cmd = String_Alloc(va("cmd class %s\n", BG_FindNameForClassNum(i)));
			uiInfo.tremAlienUpgradeList[j].infopane = UI_FindInfoPaneByName(va("%sclass", BG_FindNameForClassNum(i)));

			j++;

			uiInfo.tremAlienUpgradeCount++;
		}
	}
}

/*
===============
UI_LoadTremAlienBuilds
===============
*/
static void UI_LoadTremAlienBuilds(void)
{
	int             weapons;
	int             i, j = 0;
	stage_t         stage;

	UI_ParseCarriageList(&weapons, NULL);
	stage = UI_GetCurrentAlienStage();

	uiInfo.tremAlienBuildCount = 0;

	for(i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++)
	{
		if(BG_FindTeamForBuildable(i) == BIT_ALIENS &&
		   BG_FindBuildWeaponForBuildable(i) & weapons && BG_FindStagesForBuildable(i, stage) && BG_BuildableIsAllowed(i))
		{
			uiInfo.tremAlienBuildList[j].text = String_Alloc(BG_FindHumanNameForBuildable(i));
			uiInfo.tremAlienBuildList[j].cmd = String_Alloc(va("cmd build %s\n", BG_FindNameForBuildable(i)));
			uiInfo.tremAlienBuildList[j].infopane = UI_FindInfoPaneByName(va("%sbuild", BG_FindNameForBuildable(i)));

			j++;

			uiInfo.tremAlienBuildCount++;
		}
	}
}

/*
===============
UI_LoadTremHumanBuilds
===============
*/
static void UI_LoadTremHumanBuilds(void)
{
	int             weapons;
	int             i, j = 0;
	stage_t         stage;

	UI_ParseCarriageList(&weapons, NULL);
	stage = UI_GetCurrentHumanStage();

	uiInfo.tremHumanBuildCount = 0;

	for(i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++)
	{
		if(BG_FindTeamForBuildable(i) == BIT_HUMANS &&
		   BG_FindBuildWeaponForBuildable(i) & weapons && BG_FindStagesForBuildable(i, stage) && BG_BuildableIsAllowed(i))
		{
			uiInfo.tremHumanBuildList[j].text = String_Alloc(BG_FindHumanNameForBuildable(i));
			uiInfo.tremHumanBuildList[j].cmd = String_Alloc(va("cmd build %s\n", BG_FindNameForBuildable(i)));
			uiInfo.tremHumanBuildList[j].infopane = UI_FindInfoPaneByName(va("%sbuild", BG_FindNameForBuildable(i)));

			j++;

			uiInfo.tremHumanBuildCount++;
		}
	}
}

/*
===============
UI_LoadMods
===============
*/
static void UI_LoadMods(void)
{
	int             numdirs;
	char            dirlist[2048];
	char           *dirptr;
	char           *descptr;
	int             i;
	int             dirlen;

	uiInfo.modCount = 0;
	numdirs = trap_FS_GetFileList("$modlist", "", dirlist, sizeof(dirlist));
	dirptr = dirlist;
	for(i = 0; i < numdirs; i++)
	{
		dirlen = strlen(dirptr) + 1;
		descptr = dirptr + dirlen;
		uiInfo.modList[uiInfo.modCount].modName = String_Alloc(dirptr);
		uiInfo.modList[uiInfo.modCount].modDescr = String_Alloc(descptr);
		dirptr += dirlen + strlen(descptr) + 1;
		uiInfo.modCount++;
		if(uiInfo.modCount >= MAX_MODS)
		{
			break;
		}
	}

}


/*
===============
UI_LoadMovies
===============
*/
static void UI_LoadMovies(void)
{
	char            movielist[4096];
	char           *moviename;
	int             i, len;

	uiInfo.movieCount = trap_FS_GetFileList("video", "roq", movielist, 4096);

	if(uiInfo.movieCount)
	{
		if(uiInfo.movieCount > MAX_MOVIES)
		{
			uiInfo.movieCount = MAX_MOVIES;
		}
		moviename = movielist;
		for(i = 0; i < uiInfo.movieCount; i++)
		{
			len = strlen(moviename);
			if(!Q_stricmp(moviename + len - 4, ".roq"))
			{
				moviename[len - 4] = '\0';
			}
			Q_strupr(moviename);
			uiInfo.movieList[i] = String_Alloc(moviename);
			moviename += len + 1;
		}
	}

}



/*
===============
UI_LoadDemos
===============
*/
static void UI_LoadDemos(void)
{
	char            demolist[4096];
	char            demoExt[32];
	char           *demoname;
	int             i, len;

	Com_sprintf(demoExt, sizeof(demoExt), "dm_%d", (int)trap_Cvar_VariableValue("protocol"));

	uiInfo.demoCount = trap_FS_GetFileList("demos", demoExt, demolist, 4096);

	Com_sprintf(demoExt, sizeof(demoExt), ".dm_%d", (int)trap_Cvar_VariableValue("protocol"));

	if(uiInfo.demoCount)
	{
		if(uiInfo.demoCount > MAX_DEMOS)
		{
			uiInfo.demoCount = MAX_DEMOS;
		}
		demoname = demolist;
		for(i = 0; i < uiInfo.demoCount; i++)
		{
			len = strlen(demoname);
			if(!Q_stricmp(demoname + len - strlen(demoExt), demoExt))
			{
				demoname[len - strlen(demoExt)] = '\0';
			}
			Q_strupr(demoname);
			uiInfo.demoList[i] = String_Alloc(demoname);
			demoname += len + 1;
		}
	}

}


static qboolean UI_SetNextMap(int actual, int index)
{
	int             i;

	for(i = actual + 1; i < uiInfo.mapCount; i++)
	{
		if(uiInfo.mapList[i].active)
		{
			Menu_SetFeederSelection(NULL, FEEDER_MAPS, index + 1, "skirmish");
			return qtrue;
		}
	}
	return qfalse;
}


static void UI_StartSkirmish(qboolean next)
{
	int             i, k, g, delay, temp;
	float           skill;
	char            buff[MAX_STRING_CHARS];

	if(next)
	{
		int             actual;
		int             index = trap_Cvar_VariableValue("ui_mapIndex");

		UI_MapCountByGameType(qtrue);
		UI_SelectedMap(index, &actual);
		if(UI_SetNextMap(actual, index))
		{
		}
		else
		{
			UI_GameType_HandleKey(0, NULL, K_MOUSE1, qfalse);
			UI_MapCountByGameType(qtrue);
			Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, "skirmish");
		}
	}

	g = uiInfo.gameTypes[ui_gameType.integer].gtEnum;
	trap_Cvar_SetValue("g_gametype", g);
	trap_Cmd_ExecuteText(EXEC_APPEND, va("wait ; wait ; map %s\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName));
	skill = trap_Cvar_VariableValue("g_spSkill");
	trap_Cvar_Set("ui_scoreMap", uiInfo.mapList[ui_currentMap.integer].mapName);

	k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));

	trap_Cvar_Set("ui_singlePlayerActive", "1");

	// set up sp overrides, will be replaced on postgame
	temp = trap_Cvar_VariableValue("capturelimit");
	trap_Cvar_Set("ui_saveCaptureLimit", va("%i", temp));
	temp = trap_Cvar_VariableValue("fraglimit");
	trap_Cvar_Set("ui_saveFragLimit", va("%i", temp));

	UI_SetCapFragLimits(qfalse);

	temp = trap_Cvar_VariableValue("cg_drawTimer");
	trap_Cvar_Set("ui_drawTimer", va("%i", temp));
	temp = trap_Cvar_VariableValue("g_doWarmup");
	trap_Cvar_Set("ui_doWarmup", va("%i", temp));
	temp = trap_Cvar_VariableValue("g_friendlyFire");
	trap_Cvar_Set("ui_friendlyFire", va("%i", temp));
	temp = trap_Cvar_VariableValue("sv_maxClients");
	trap_Cvar_Set("ui_maxClients", va("%i", temp));
	temp = trap_Cvar_VariableValue("g_warmup");
	trap_Cvar_Set("ui_Warmup", va("%i", temp));
	temp = trap_Cvar_VariableValue("sv_pure");
	trap_Cvar_Set("ui_pure", va("%i", temp));

	trap_Cvar_Set("cg_cameraOrbit", "0");
	trap_Cvar_Set("cg_thirdPerson", "0");
	trap_Cvar_Set("cg_drawTimer", "1");
	trap_Cvar_Set("g_doWarmup", "1");
	trap_Cvar_Set("g_warmup", "15");
	trap_Cvar_Set("sv_pure", "0");
	trap_Cvar_Set("g_friendlyFire", "0");
	trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName"));
	trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName"));

	if(trap_Cvar_VariableValue("ui_recordSPDemo"))
	{
		Com_sprintf(buff, MAX_STRING_CHARS, "%s_%i", uiInfo.mapList[ui_currentMap.integer].mapLoadName, g);
		trap_Cvar_Set("ui_recordSPDemoName", buff);
	}

	delay = 500;

	{
		temp = uiInfo.mapList[ui_currentMap.integer].teamMembers * 2;
		trap_Cvar_Set("sv_maxClients", va("%d", temp));
		for(i = 0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers; i++)
		{
			Com_sprintf(buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill,
						"", delay, uiInfo.teamList[k].teamMembers[i]);
			trap_Cmd_ExecuteText(EXEC_APPEND, buff);
			delay += 500;
		}
		k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
		for(i = 0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers - 1; i++)
		{
			Com_sprintf(buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill,
						"", delay, uiInfo.teamList[k].teamMembers[i]);
			trap_Cmd_ExecuteText(EXEC_APPEND, buff);
			delay += 500;
		}
	}
}

static void UI_Update(const char *name)
{
	int             val = trap_Cvar_VariableValue(name);

	if(Q_stricmp(name, "ui_SetName") == 0)
	{
		trap_Cvar_Set("name", UI_Cvar_VariableString("ui_Name"));
	}
	else if(Q_stricmp(name, "ui_setRate") == 0)
	{
		float           rate = trap_Cvar_VariableValue("rate");

		if(rate >= 5000)
		{
			trap_Cvar_Set("cl_maxpackets", "30");
			trap_Cvar_Set("cl_packetdup", "1");
		}
		else if(rate >= 4000)
		{
			trap_Cvar_Set("cl_maxpackets", "15");
			trap_Cvar_Set("cl_packetdup", "2");	// favor less prediction errors when there's packet loss
		}
		else
		{
			trap_Cvar_Set("cl_maxpackets", "15");
			trap_Cvar_Set("cl_packetdup", "1");	// favor lower bandwidth
		}
	}
	else if(Q_stricmp(name, "ui_GetName") == 0)
	{
		trap_Cvar_Set("ui_Name", UI_Cvar_VariableString("name"));
	}
	else if(Q_stricmp(name, "r_colorbits") == 0)
	{
		switch (val)
		{
			case 0:
				trap_Cvar_SetValue("r_depthbits", 0);
				trap_Cvar_SetValue("r_stencilbits", 0);
				break;
			case 16:
				trap_Cvar_SetValue("r_depthbits", 16);
				trap_Cvar_SetValue("r_stencilbits", 0);
				break;
			case 32:
				trap_Cvar_SetValue("r_depthbits", 24);
				break;
		}
	}
	else if(Q_stricmp(name, "r_lodbias") == 0)
	{
		switch (val)
		{
			case 0:
				trap_Cvar_SetValue("r_subdivisions", 4);
				break;
			case 1:
				trap_Cvar_SetValue("r_subdivisions", 12);
				break;
			case 2:
				trap_Cvar_SetValue("r_subdivisions", 20);
				break;
		}
	}
	else if(Q_stricmp(name, "r_shadowMapQuality") == 0)
	{
		switch (val)
		{
			case 1:				// low
				trap_Cvar_SetValue("r_shadowMapSizeUltra", 256);
				trap_Cvar_SetValue("r_shadowMapSizeVeryHigh", 128);
				trap_Cvar_SetValue("r_shadowMapSizeHigh", 64);
				trap_Cvar_SetValue("r_shadowMapSizeMedium", 32);
				trap_Cvar_SetValue("r_shadowMapSizeLow", 16);
				break;
			case 2:				// medium
				trap_Cvar_SetValue("r_shadowMapSizeUltra", 512);
				trap_Cvar_SetValue("r_shadowMapSizeVeryHigh", 256);
				trap_Cvar_SetValue("r_shadowMapSizeHigh", 128);
				trap_Cvar_SetValue("r_shadowMapSizeMedium", 64);
				trap_Cvar_SetValue("r_shadowMapSizeLow", 32);
				break;
			case 3:				// high
				trap_Cvar_SetValue("r_shadowMapSizeUltra", 1024);
				trap_Cvar_SetValue("r_shadowMapSizeVeryHigh", 512);
				trap_Cvar_SetValue("r_shadowMapSizeHigh", 256);
				trap_Cvar_SetValue("r_shadowMapSizeMedium", 128);
				trap_Cvar_SetValue("r_shadowMapSizeLow", 64);
				break;
			case 4:				// very high
				trap_Cvar_SetValue("r_shadowMapSizeUltra", 2048);
				trap_Cvar_SetValue("r_shadowMapSizeVeryHigh", 1024);
				trap_Cvar_SetValue("r_shadowMapSizeHigh", 512);
				trap_Cvar_SetValue("r_shadowMapSizeMedium", 256);
				trap_Cvar_SetValue("r_shadowMapSizeLow", 128);
				break;
				/*
				   case 5: // ultra
				   trap_Cvar_SetValue("r_shadowMapSizeUltra", 2048);
				   trap_Cvar_SetValue("r_shadowMapSizeVeryHigh", 2048);
				   trap_Cvar_SetValue("r_shadowMapSizeHigh", 1024);
				   trap_Cvar_SetValue("r_shadowMapSizeMedium", 512);
				   trap_Cvar_SetValue("r_shadowMapSizeLow", 128);
				   break;
				 */
			case 0:				// custom
			default:
				break;
		}
	}
	else if(Q_stricmp(name, "cg_shadows") == 0)
	{
		switch (val)
		{
			case 0:
			case 1:
			case 2:
				trap_Cvar_SetValue("r_dynamicLightsCastShadows", 0);
			case 3:
				trap_Cvar_SetValue("r_softShadows", 0);
				trap_Cvar_SetValue("r_shadowBlur", 1);
				trap_Cvar_SetValue("r_shadowMapQuality", 0);
				break;
		}
	}
	else if(Q_stricmp(name, "r_normalMapping") == 0)
	{
		switch (val)
		{
			case 0:
				trap_Cvar_SetValue("r_parallaxMapping", 0);
				break;
		}
	}
	else if(Q_stricmp(name, "ui_glCustom") == 0)
	{
		switch (val)
		{
			case 0:			// high quality
				trap_Cvar_SetValue("r_fullScreen", 1);
				trap_Cvar_SetValue("r_subdivisions", 4);
				trap_Cvar_SetValue("r_vertexlight", 0);
				trap_Cvar_SetValue("r_lodbias", 0);
				trap_Cvar_SetValue("r_colorbits", 32);
				trap_Cvar_SetValue("r_depthbits", 24);
				trap_Cvar_SetValue("r_picmip", 0);
				trap_Cvar_SetValue("r_mode", 4);
				trap_Cvar_SetValue("r_texturebits", 32);
				trap_Cvar_SetValue("r_fastSky", 0);
				trap_Cvar_SetValue("r_inGameVideo", 1);
				trap_Cvar_SetValue("cg_shadows", 1);
				trap_Cvar_SetValue("cg_brassTime", 2500);
				trap_Cvar_Set("r_texturemode", "GL_LINEAR_MIPMAP_LINEAR");
				break;
			case 1:			// normal
				trap_Cvar_SetValue("r_fullScreen", 1);
				trap_Cvar_SetValue("r_subdivisions", 12);
				trap_Cvar_SetValue("r_vertexlight", 0);
				trap_Cvar_SetValue("r_lodbias", 0);
				trap_Cvar_SetValue("r_colorbits", 0);
				trap_Cvar_SetValue("r_depthbits", 24);
				trap_Cvar_SetValue("r_picmip", 1);
				trap_Cvar_SetValue("r_mode", 3);
				trap_Cvar_SetValue("r_texturebits", 0);
				trap_Cvar_SetValue("r_fastSky", 0);
				trap_Cvar_SetValue("r_inGameVideo", 1);
				trap_Cvar_SetValue("cg_brassTime", 2500);
				trap_Cvar_Set("r_texturemode", "GL_LINEAR_MIPMAP_LINEAR");
				trap_Cvar_SetValue("cg_shadows", 0);
				break;
			case 2:			// fast
				trap_Cvar_SetValue("r_fullScreen", 1);
				trap_Cvar_SetValue("r_subdivisions", 8);
				trap_Cvar_SetValue("r_vertexlight", 0);
				trap_Cvar_SetValue("r_lodbias", 1);
				trap_Cvar_SetValue("r_colorbits", 0);
				trap_Cvar_SetValue("r_depthbits", 0);
				trap_Cvar_SetValue("r_picmip", 1);
				trap_Cvar_SetValue("r_mode", 3);
				trap_Cvar_SetValue("r_texturebits", 0);
				trap_Cvar_SetValue("cg_shadows", 0);
				trap_Cvar_SetValue("r_fastSky", 1);
				trap_Cvar_SetValue("r_inGameVideo", 0);
				trap_Cvar_SetValue("cg_brassTime", 0);
				trap_Cvar_Set("r_texturemode", "GL_LINEAR_MIPMAP_NEAREST");
				break;
			case 3:			// fastest
				trap_Cvar_SetValue("r_fullScreen", 1);
				trap_Cvar_SetValue("r_subdivisions", 20);
				trap_Cvar_SetValue("r_vertexlight", 1);
				trap_Cvar_SetValue("r_lodbias", 2);
				trap_Cvar_SetValue("r_colorbits", 16);
				trap_Cvar_SetValue("r_depthbits", 16);
				trap_Cvar_SetValue("r_mode", 3);
				trap_Cvar_SetValue("r_picmip", 2);
				trap_Cvar_SetValue("r_texturebits", 16);
				trap_Cvar_SetValue("cg_shadows", 0);
				trap_Cvar_SetValue("cg_brassTime", 0);
				trap_Cvar_SetValue("r_fastSky", 1);
				trap_Cvar_SetValue("r_inGameVideo", 0);
				trap_Cvar_Set("r_texturemode", "GL_LINEAR_MIPMAP_NEAREST");
				break;
		}
	}
	else if(Q_stricmp(name, "ui_mousePitch") == 0)
	{
		if(val == 0)
		{
			trap_Cvar_SetValue("m_pitch", 0.022f);
		}
		else
		{
			trap_Cvar_SetValue("m_pitch", -0.022f);
		}
	}
}

static void UI_RunMenuScript(char **args)
{
	const char     *name, *name2;
	char            buff[1024];
	const char     *cmd;

	if(String_Parse(args, &name))
	{
		if(Q_stricmp(name, "StartServer") == 0)
		{
			int             i, clients, oldclients;
			float           skill;

			trap_Cvar_Set("cg_thirdPerson", "0");
			trap_Cvar_Set("cg_cameraOrbit", "0");
			trap_Cvar_Set("ui_singlePlayerActive", "0");
			trap_Cvar_SetValue("dedicated", Com_Clamp(0, 2, ui_dedicated.integer));
			trap_Cvar_SetValue("g_gametype", Com_Clamp(0, 8, uiInfo.gameTypes[ui_netGameType.integer].gtEnum));
			trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName"));
			trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName"));
			trap_Cmd_ExecuteText(EXEC_APPEND, va("wait ; wait ; map %s\n", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName));
			skill = trap_Cvar_VariableValue("g_spSkill");
			// set max clients based on spots
			oldclients = trap_Cvar_VariableValue("sv_maxClients");
			clients = 0;
			for(i = 0; i < PLAYERS_PER_TEAM; i++)
			{
				int             bot = trap_Cvar_VariableValue(va("ui_blueteam%i", i + 1));

				if(bot >= 0)
				{
					clients++;
				}
				bot = trap_Cvar_VariableValue(va("ui_redteam%i", i + 1));
				if(bot >= 0)
				{
					clients++;
				}
			}
			if(clients == 0)
			{
				clients = 8;
			}

			if(oldclients > clients)
			{
				clients = oldclients;
			}

			trap_Cvar_Set("sv_maxClients", va("%d", clients));

			for(i = 0; i < PLAYERS_PER_TEAM; i++)
			{
				int             bot = trap_Cvar_VariableValue(va("ui_blueteam%i", i + 1));

				if(bot > 1)
				{
					Com_sprintf(buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot - 2), skill);
					trap_Cmd_ExecuteText(EXEC_APPEND, buff);
				}
				bot = trap_Cvar_VariableValue(va("ui_redteam%i", i + 1));
				if(bot > 1)
				{
					Com_sprintf(buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot - 2), skill);
					trap_Cmd_ExecuteText(EXEC_APPEND, buff);
				}
			}
		}
		else if(Q_stricmp(name, "updateSPMenu") == 0)
		{
			UI_SetCapFragLimits(qtrue);
			UI_MapCountByGameType(qtrue);
			ui_mapIndex.integer = UI_GetIndexFromSelection(ui_currentMap.integer);
			trap_Cvar_Set("ui_mapIndex", va("%d", ui_mapIndex.integer));
			Menu_SetFeederSelection(NULL, FEEDER_MAPS, ui_mapIndex.integer, "skirmish");
			UI_GameType_HandleKey(0, NULL, K_MOUSE1, qfalse);
			UI_GameType_HandleKey(0, NULL, K_MOUSE2, qfalse);
		}
		else if(Q_stricmp(name, "resetDefaults") == 0)
		{
			trap_Cmd_ExecuteText(EXEC_APPEND, "exec default.cfg\n");
			trap_Cmd_ExecuteText(EXEC_APPEND, "cvar_restart\n");
			Controls_SetDefaults();
			trap_Cvar_Set("com_introPlayed", "1");
			trap_Cmd_ExecuteText(EXEC_APPEND, "vid_restart\n");
		}
		else if(Q_stricmp(name, "loadArenas") == 0)
		{
			UI_LoadArenas();
			UI_MapCountByGameType(qfalse);
			Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver");
		}
		else if(Q_stricmp(name, "saveControls") == 0)
		{
			Controls_SetConfig(qtrue);
		}
		else if(Q_stricmp(name, "loadControls") == 0)
		{
			Controls_GetConfig();
		}
		else if(Q_stricmp(name, "clearError") == 0)
		{
			trap_Cvar_Set("com_errorMessage", "");
		}
		else if(Q_stricmp(name, "loadGameInfo") == 0)
		{
/*      UI_ParseGameInfo("gameinfo.txt");
      UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);*/
		}
		else if(Q_stricmp(name, "resetScores") == 0)
		{
			UI_ClearScores();
		}
		else if(Q_stricmp(name, "RefreshServers") == 0)
		{
			UI_StartServerRefresh(qtrue);
			UI_BuildServerDisplayList(qtrue);
		}
		else if(Q_stricmp(name, "RefreshFilter") == 0)
		{
			UI_StartServerRefresh(qfalse);
			UI_BuildServerDisplayList(qtrue);
		}
		else if(Q_stricmp(name, "RunSPDemo") == 0)
		{
			if(uiInfo.demoAvailable)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND,
									 va("demo %s_%i\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName,
										uiInfo.gameTypes[ui_gameType.integer].gtEnum));
			}
		}
		else if(Q_stricmp(name, "LoadDemos") == 0)
		{
			UI_LoadDemos();
		}
		else if(Q_stricmp(name, "LoadMovies") == 0)
		{
			UI_LoadMovies();
		}
		else if(Q_stricmp(name, "LoadMods") == 0)
		{
			UI_LoadMods();
		}

//TA: tremulous menus
		else if(Q_stricmp(name, "LoadTeams") == 0)
			UI_LoadTremTeams();
		else if(Q_stricmp(name, "JoinTeam") == 0)
		{
			if((cmd = uiInfo.tremTeamList[uiInfo.tremTeamIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadHumanItems") == 0)
			UI_LoadTremHumanItems();
		else if(Q_stricmp(name, "SpawnWithHumanItem") == 0)
		{
			if((cmd = uiInfo.tremHumanItemList[uiInfo.tremHumanItemIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadAlienClasses") == 0)
			UI_LoadTremAlienClasses();
		else if(Q_stricmp(name, "SpawnAsAlienClass") == 0)
		{
			if((cmd = uiInfo.tremAlienClassList[uiInfo.tremAlienClassIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadHumanArmouryBuys") == 0)
			UI_LoadTremHumanArmouryBuys();
		else if(Q_stricmp(name, "BuyFromArmoury") == 0)
		{
			if((cmd = uiInfo.tremHumanArmouryBuyList[uiInfo.tremHumanArmouryBuyIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadHumanArmourySells") == 0)
			UI_LoadTremHumanArmourySells();
		else if(Q_stricmp(name, "SellToArmoury") == 0)
		{
			if((cmd = uiInfo.tremHumanArmourySellList[uiInfo.tremHumanArmourySellIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadAlienUpgrades") == 0)
		{
			UI_LoadTremAlienUpgrades();

			//disallow the menu if it would be empty
			if(uiInfo.tremAlienUpgradeCount <= 0)
				Menus_CloseAll();
		}
		else if(Q_stricmp(name, "UpgradeToNewClass") == 0)
		{
			if((cmd = uiInfo.tremAlienUpgradeList[uiInfo.tremAlienUpgradeIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadAlienBuilds") == 0)
			UI_LoadTremAlienBuilds();
		else if(Q_stricmp(name, "BuildAlienBuildable") == 0)
		{
			if((cmd = uiInfo.tremAlienBuildList[uiInfo.tremAlienBuildIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "LoadHumanBuilds") == 0)
			UI_LoadTremHumanBuilds();
		else if(Q_stricmp(name, "BuildHumanBuildable") == 0)
		{
			if((cmd = uiInfo.tremHumanBuildList[uiInfo.tremHumanBuildIndex].cmd))
				trap_Cmd_ExecuteText(EXEC_APPEND, cmd);
		}
		else if(Q_stricmp(name, "PTRCRestore") == 0)
		{
			int             len;
			char            text[16];
			fileHandle_t    f;
			char            command[32];

			// load the file
			len = trap_FS_FOpenFile("ptrc.cfg", &f, FS_READ);

			if(len > 0 && (len < sizeof(text) - 1))
			{
				trap_FS_Read(text, len, f);
				text[len] = 0;
				trap_FS_FCloseFile(f);

				Com_sprintf(command, 32, "ptrcrestore %s", text);

				trap_Cmd_ExecuteText(EXEC_APPEND, command);
			}
		}
//TA: tremulous menus

		else if(Q_stricmp(name, "playMovie") == 0)
		{
			if(uiInfo.previewMovie >= 0)
			{
				trap_CIN_StopCinematic(uiInfo.previewMovie);
			}
			trap_Cmd_ExecuteText(EXEC_APPEND, va("cinematic %s.roq 2\n", uiInfo.movieList[uiInfo.movieIndex]));
		}
		else if(Q_stricmp(name, "RunMod") == 0)
		{
			trap_Cvar_Set("fs_game", uiInfo.modList[uiInfo.modIndex].modName);
			trap_Cmd_ExecuteText(EXEC_APPEND, "vid_restart;");
		}
		else if(Q_stricmp(name, "RunDemo") == 0)
		{
			trap_Cmd_ExecuteText(EXEC_APPEND, va("demo %s\n", uiInfo.demoList[uiInfo.demoIndex]));
		}
		else if(Q_stricmp(name, "Tremulous") == 0)
		{
			trap_Cvar_Set("fs_game", "");
			trap_Cmd_ExecuteText(EXEC_APPEND, "vid_restart;");
		}
		else if(Q_stricmp(name, "closeJoin") == 0)
		{
			if(uiInfo.serverStatus.refreshActive)
			{
				UI_StopServerRefresh();
				uiInfo.serverStatus.nextDisplayRefresh = 0;
				uiInfo.nextServerStatusRefresh = 0;
				uiInfo.nextFindPlayerRefresh = 0;
				UI_BuildServerDisplayList(qtrue);
			}
			else
			{
				Menus_CloseByName("joinserver");
				Menus_OpenByName("main");
			}
		}
		else if(Q_stricmp(name, "StopRefresh") == 0)
		{
			UI_StopServerRefresh();
			uiInfo.serverStatus.nextDisplayRefresh = 0;
			uiInfo.nextServerStatusRefresh = 0;
			uiInfo.nextFindPlayerRefresh = 0;
		}
		else if(Q_stricmp(name, "UpdateFilter") == 0)
		{
			if(ui_netSource.integer == AS_LOCAL)
			{
				UI_StartServerRefresh(qtrue);
			}
			UI_BuildServerDisplayList(qtrue);
			UI_FeederSelection(FEEDER_SERVERS, 0);
		}
		else if(Q_stricmp(name, "ServerStatus") == 0)
		{
			trap_LAN_GetServerAddressString(ui_netSource.integer,
											uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer],
											uiInfo.serverStatusAddress, sizeof(uiInfo.serverStatusAddress));
			UI_BuildServerStatus(qtrue);
		}
		else if(Q_stricmp(name, "FoundPlayerServerStatus") == 0)
		{
			Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer],
					   sizeof(uiInfo.serverStatusAddress));
			UI_BuildServerStatus(qtrue);
			Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL);
		}
		else if(Q_stricmp(name, "FindPlayer") == 0)
		{
			UI_BuildFindPlayerList(qtrue);
			// clear the displayed server status info
			uiInfo.serverStatusInfo.numLines = 0;
			Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL);
		}
		else if(Q_stricmp(name, "JoinServer") == 0)
		{
			trap_Cvar_Set("cg_thirdPerson", "0");
			trap_Cvar_Set("cg_cameraOrbit", "0");
			trap_Cvar_Set("ui_singlePlayerActive", "0");
			if(uiInfo.serverStatus.currentServer >= 0 &&
			   uiInfo.serverStatus.currentServer < uiInfo.serverStatus.numDisplayServers)
			{
				trap_LAN_GetServerAddressString(ui_netSource.integer,
												uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff,
												1024);
				trap_Cmd_ExecuteText(EXEC_APPEND, va("connect %s\n", buff));
			}
		}
		else if(Q_stricmp(name, "FoundPlayerJoinServer") == 0)
		{
			trap_Cvar_Set("ui_singlePlayerActive", "0");
			if(uiInfo.currentFoundPlayerServer >= 0 && uiInfo.currentFoundPlayerServer < uiInfo.numFoundPlayerServers)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND,
									 va("connect %s\n", uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer]));
			}
		}
		else if(Q_stricmp(name, "Quit") == 0)
		{
			trap_Cvar_Set("ui_singlePlayerActive", "0");
			trap_Cmd_ExecuteText(EXEC_NOW, "quit");
		}
		else if(Q_stricmp(name, "Controls") == 0)
		{
			trap_Cvar_Set("cl_paused", "1");
			trap_Key_SetCatcher(KEYCATCH_UI);
			Menus_CloseAll();
			Menus_ActivateByName("setup_menu2");
		}
		else if(Q_stricmp(name, "Leave") == 0)
		{
			trap_Cmd_ExecuteText(EXEC_APPEND, "disconnect\n");
			trap_Key_SetCatcher(KEYCATCH_UI);
			Menus_CloseAll();
			Menus_ActivateByName("main");
		}
		else if(Q_stricmp(name, "ServerSort") == 0)
		{
			int             sortColumn;

			if(Int_Parse(args, &sortColumn))
			{
				// if same column we're already sorting on then flip the direction
				if(sortColumn == uiInfo.serverStatus.sortKey)
				{
					uiInfo.serverStatus.sortDir = !uiInfo.serverStatus.sortDir;
				}
				// make sure we sort again
				UI_ServersSort(sortColumn, qtrue);
			}
		}
		else if(Q_stricmp(name, "nextSkirmish") == 0)
		{
			UI_StartSkirmish(qtrue);
		}
		else if(Q_stricmp(name, "SkirmishStart") == 0)
		{
			UI_StartSkirmish(qfalse);
		}
		else if(Q_stricmp(name, "closeingame") == 0)
		{
			trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
			trap_Key_ClearStates();
			trap_Cvar_Set("cl_paused", "0");
			Menus_CloseAll();
		}
		else if(Q_stricmp(name, "voteMap") == 0)
		{
			if(ui_currentNetMap.integer >= 0 && ui_currentNetMap.integer < uiInfo.mapCount)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND, va("callvote map %s\n", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName));
			}
		}
		else if(Q_stricmp(name, "voteKick") == 0)
		{
			if(uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND, va("callvote kick %s\n", uiInfo.playerNames[uiInfo.playerIndex]));
			}
		}
		else if(Q_stricmp(name, "voteTeamKick") == 0)
		{
			if(uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND, va("callteamvote teamkick %s\n", uiInfo.playerNames[uiInfo.teamIndex]));
			}
		}
		else if(Q_stricmp(name, "voteGame") == 0)
		{
			if(ui_netGameType.integer >= 0 && ui_netGameType.integer < uiInfo.numGameTypes)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND,
									 va("callvote g_gametype %i\n", uiInfo.gameTypes[ui_netGameType.integer].gtEnum));
			}
		}
		else if(Q_stricmp(name, "voteLeader") == 0)
		{
			if(uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount)
			{
				trap_Cmd_ExecuteText(EXEC_APPEND, va("callteamvote leader %s\n", uiInfo.teamNames[uiInfo.teamIndex]));
			}
		}
		else if(Q_stricmp(name, "addBot") == 0)
		{
			trap_Cmd_ExecuteText(EXEC_APPEND,
								 va("addbot %s %i %s\n", UI_GetBotNameByNumber(uiInfo.botIndex), uiInfo.skillIndex + 1,
									(uiInfo.redBlue == 0) ? "Red" : "Blue"));
		}
		else if(Q_stricmp(name, "addFavorite") == 0)
		{
			if(ui_netSource.integer != AS_FAVORITES)
			{
				char            name[MAX_NAME_LENGTH];
				char            addr[MAX_NAME_LENGTH];
				int             res;

				trap_LAN_GetServerInfo(ui_netSource.integer,
									   uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff,
									   MAX_STRING_CHARS);
				name[0] = addr[0] = '\0';
				Q_strncpyz(name, Info_ValueForKey(buff, "hostname"), MAX_NAME_LENGTH);
				Q_strncpyz(addr, Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH);
				if(strlen(name) > 0 && strlen(addr) > 0)
				{
					res = trap_LAN_AddServer(AS_FAVORITES, name, addr);
					if(res == 0)
					{
						// server already in the list
						Com_Printf("Favorite already in list\n");
					}
					else if(res == -1)
					{
						// list full
						Com_Printf("Favorite list full\n");
					}
					else
					{
						// successfully added
						Com_Printf("Added favorite server %s\n", addr);
					}
				}
			}
		}
		else if(Q_stricmp(name, "deleteFavorite") == 0)
		{
			if(ui_netSource.integer == AS_FAVORITES)
			{
				char            addr[MAX_NAME_LENGTH];

				trap_LAN_GetServerInfo(ui_netSource.integer,
									   uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff,
									   MAX_STRING_CHARS);
				addr[0] = '\0';
				Q_strncpyz(addr, Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH);
				if(strlen(addr) > 0)
				{
					trap_LAN_RemoveServer(AS_FAVORITES, addr);
				}
			}
		}
		else if(Q_stricmp(name, "createFavorite") == 0)
		{
			if(ui_netSource.integer == AS_FAVORITES)
			{
				char            name[MAX_NAME_LENGTH];
				char            addr[MAX_NAME_LENGTH];
				int             res;

				name[0] = addr[0] = '\0';
				Q_strncpyz(name, UI_Cvar_VariableString("ui_favoriteName"), MAX_NAME_LENGTH);
				Q_strncpyz(addr, UI_Cvar_VariableString("ui_favoriteAddress"), MAX_NAME_LENGTH);
				if(strlen(name) > 0 && strlen(addr) > 0)
				{
					res = trap_LAN_AddServer(AS_FAVORITES, name, addr);
					if(res == 0)
					{
						// server already in the list
						Com_Printf("Favorite already in list\n");
					}
					else if(res == -1)
					{
						// list full
						Com_Printf("Favorite list full\n");
					}
					else
					{
						// successfully added
						Com_Printf("Added favorite server %s\n", addr);
					}
				}
			}
		}
		else if(Q_stricmp(name, "orders") == 0)
		{
			const char     *orders;

			if(String_Parse(args, &orders))
			{
				int             selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer");

				if(selectedPlayer < uiInfo.myTeamCount)
				{
					strcpy(buff, orders);
					trap_Cmd_ExecuteText(EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]));
					trap_Cmd_ExecuteText(EXEC_APPEND, "\n");
				}
				else
				{
					int             i;

					for(i = 0; i < uiInfo.myTeamCount; i++)
					{
						if(Q_stricmp(UI_Cvar_VariableString("name"), uiInfo.teamNames[i]) == 0)
						{
							continue;
						}
						strcpy(buff, orders);
						trap_Cmd_ExecuteText(EXEC_APPEND, va(buff, uiInfo.teamNames[i]));
						trap_Cmd_ExecuteText(EXEC_APPEND, "\n");
					}
				}
				trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
				trap_Key_ClearStates();
				trap_Cvar_Set("cl_paused", "0");
				Menus_CloseAll();
			}
		}
		else if(Q_stricmp(name, "voiceOrdersTeam") == 0)
		{
			const char     *orders;

			if(String_Parse(args, &orders))
			{
				int             selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer");

				if(selectedPlayer == uiInfo.myTeamCount)
				{
					trap_Cmd_ExecuteText(EXEC_APPEND, orders);
					trap_Cmd_ExecuteText(EXEC_APPEND, "\n");
				}
				trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
				trap_Key_ClearStates();
				trap_Cvar_Set("cl_paused", "0");
				Menus_CloseAll();
			}
		}
		else if(Q_stricmp(name, "voiceOrders") == 0)
		{
			const char     *orders;

			if(String_Parse(args, &orders))
			{
				int             selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer");

				if(selectedPlayer < uiInfo.myTeamCount)
				{
					strcpy(buff, orders);
					trap_Cmd_ExecuteText(EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]));
					trap_Cmd_ExecuteText(EXEC_APPEND, "\n");
				}
				trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
				trap_Key_ClearStates();
				trap_Cvar_Set("cl_paused", "0");
				Menus_CloseAll();
			}
		}
		else if(Q_stricmp(name, "glCustom") == 0)
		{
			trap_Cvar_Set("ui_glCustom", "4");
		}
		else if(Q_stricmp(name, "update") == 0)
		{
			if(String_Parse(args, &name2))
				UI_Update(name2);
		}
		else if(Q_stricmp(name, "setPbClStatus") == 0)
		{
			int             stat;

			if(Int_Parse(args, &stat))
				; //trap_SetPbClStatus(stat);
		}
		else
		{
			Com_Printf("unknown UI script %s\n", name);
		}
	}
}

static void UI_GetTeamColor(vec4_t * color)
{
}

/*
==================
UI_MapCountByGameType
==================
*/
static int UI_MapCountByGameType(qboolean singlePlayer)
{
	int             i, c, game;

	c = 0;
	game = singlePlayer ? uiInfo.gameTypes[ui_gameType.integer].gtEnum : uiInfo.gameTypes[ui_netGameType.integer].gtEnum;

	for(i = 0; i < uiInfo.mapCount; i++)
	{
		uiInfo.mapList[i].active = qfalse;
		if(uiInfo.mapList[i].typeBits & (1 << game))
		{
			if(singlePlayer)
			{
				if(!(uiInfo.mapList[i].typeBits & (1 << 2)))
				{
					continue;
				}
			}
			c++;
			uiInfo.mapList[i].active = qtrue;
		}
	}
	return c;
}

qboolean UI_hasSkinForBase(const char *base, const char *team)
{
	char            test[1024];

	Com_sprintf(test, sizeof(test), "models/players/%s/%s/lower_default.skin", base, team);

	if(trap_FS_FOpenFile(test, NULL, FS_READ))
	{
		return qtrue;
	}
	Com_sprintf(test, sizeof(test), "models/players/characters/%s/%s/lower_default.skin", base, team);

	if(trap_FS_FOpenFile(test, NULL, FS_READ))
	{
		return qtrue;
	}
	return qfalse;
}

/*
==================
UI_MapCountByTeam
==================
*/
static int UI_HeadCountByTeam(void)
{
	static int      init = 0;
	int             i, j, k, c, tIndex;

	c = 0;
	if(!init)
	{
		for(i = 0; i < uiInfo.characterCount; i++)
		{
			uiInfo.characterList[i].reference = 0;
			for(j = 0; j < uiInfo.teamCount; j++)
			{
				if(UI_hasSkinForBase(uiInfo.characterList[i].base, uiInfo.teamList[j].teamName))
				{
					uiInfo.characterList[i].reference |= (1 << j);
				}
			}
		}
		init = 1;
	}

	tIndex = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

	// do names
	for(i = 0; i < uiInfo.characterCount; i++)
	{
		uiInfo.characterList[i].active = qfalse;
		for(j = 0; j < TEAM_MEMBERS; j++)
		{
			if(uiInfo.teamList[tIndex].teamMembers[j] != NULL)
			{
				if(uiInfo.characterList[i].reference & (1 << tIndex))
				{				// && Q_stricmp(uiInfo.teamList[tIndex].teamMembers[j], uiInfo.characterList[i].name)==0) {
					uiInfo.characterList[i].active = qtrue;
					c++;
					break;
				}
			}
		}
	}

	// and then aliases
	for(j = 0; j < TEAM_MEMBERS; j++)
	{
		for(k = 0; k < uiInfo.aliasCount; k++)
		{
			if(uiInfo.aliasList[k].name != NULL)
			{
				if(Q_stricmp(uiInfo.teamList[tIndex].teamMembers[j], uiInfo.aliasList[k].name) == 0)
				{
					for(i = 0; i < uiInfo.characterCount; i++)
					{
						if(uiInfo.characterList[i].headImage != -1 && uiInfo.characterList[i].reference & (1 << tIndex) &&
						   Q_stricmp(uiInfo.aliasList[k].ai, uiInfo.characterList[i].name) == 0)
						{
							if(uiInfo.characterList[i].active == qfalse)
							{
								uiInfo.characterList[i].active = qtrue;
								c++;
							}
							break;
						}
					}
				}
			}
		}
	}
	return c;
}

/*
==================
UI_InsertServerIntoDisplayList
==================
*/
static void UI_InsertServerIntoDisplayList(int num, int position)
{
	int             i;

	if(position < 0 || position > uiInfo.serverStatus.numDisplayServers)
	{
		return;
	}
	//
	uiInfo.serverStatus.numDisplayServers++;
	for(i = uiInfo.serverStatus.numDisplayServers; i > position; i--)
	{
		uiInfo.serverStatus.displayServers[i] = uiInfo.serverStatus.displayServers[i - 1];
	}
	uiInfo.serverStatus.displayServers[position] = num;
}

/*
==================
UI_RemoveServerFromDisplayList
==================
*/
static void UI_RemoveServerFromDisplayList(int num)
{
	int             i, j;

	for(i = 0; i < uiInfo.serverStatus.numDisplayServers; i++)
	{
		if(uiInfo.serverStatus.displayServers[i] == num)
		{
			uiInfo.serverStatus.numDisplayServers--;
			for(j = i; j < uiInfo.serverStatus.numDisplayServers; j++)
			{
				uiInfo.serverStatus.displayServers[j] = uiInfo.serverStatus.displayServers[j + 1];
			}
			return;
		}
	}
}

/*
==================
UI_BinaryServerInsertion
==================
*/
static void UI_BinaryServerInsertion(int num)
{
	int             mid, offset, res, len;

	// use binary search to insert server
	len = uiInfo.serverStatus.numDisplayServers;
	mid = len;
	offset = 0;
	res = 0;
	while(mid > 0)
	{
		mid = len >> 1;
		//
		res = trap_LAN_CompareServers(ui_netSource.integer, uiInfo.serverStatus.sortKey,
									  uiInfo.serverStatus.sortDir, num, uiInfo.serverStatus.displayServers[offset + mid]);
		// if equal
		if(res == 0)
		{
			UI_InsertServerIntoDisplayList(num, offset + mid);
			return;
		}
		// if larger
		else if(res == 1)
		{
			offset += mid;
			len -= mid;
		}
		// if smaller
		else
		{
			len -= mid;
		}
	}
	if(res == 1)
	{
		offset++;
	}
	UI_InsertServerIntoDisplayList(num, offset);
}

/*
==================
UI_BuildServerDisplayList
==================
*/
static void UI_BuildServerDisplayList(qboolean force)
{
	int             i, count, clients, maxClients, ping, game, len, visible;
	char            info[MAX_STRING_CHARS];

//  qboolean startRefresh = qtrue; TTimo: unused
	static int      numinvisible;

	if(!(force || uiInfo.uiDC.realTime > uiInfo.serverStatus.nextDisplayRefresh))
	{
		return;
	}
	// if we shouldn't reset
	if(force == 2)
	{
		force = 0;
	}

	// do motd updates here too
	trap_Cvar_VariableStringBuffer("cl_motdString", uiInfo.serverStatus.motd, sizeof(uiInfo.serverStatus.motd));
	len = strlen(uiInfo.serverStatus.motd);
	if(len != uiInfo.serverStatus.motdLen)
	{
		uiInfo.serverStatus.motdLen = len;
		uiInfo.serverStatus.motdWidth = -1;
	}

	if(force)
	{
		numinvisible = 0;
		// clear number of displayed servers
		uiInfo.serverStatus.numDisplayServers = 0;
		uiInfo.serverStatus.numPlayersOnServers = 0;
		// set list box index to zero
		Menu_SetFeederSelection(NULL, FEEDER_SERVERS, 0, NULL);
		// mark all servers as visible so we store ping updates for them
		trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue);
	}

	// get the server count (comes from the master)
	count = trap_LAN_GetServerCount(ui_netSource.integer);
	if(count == -1 || (ui_netSource.integer == AS_LOCAL && count == 0))
	{
		// still waiting on a response from the master
		uiInfo.serverStatus.numDisplayServers = 0;
		uiInfo.serverStatus.numPlayersOnServers = 0;
		uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 500;
		return;
	}

	visible = qfalse;
	for(i = 0; i < count; i++)
	{
		// if we already got info for this server
		if(!trap_LAN_ServerIsVisible(ui_netSource.integer, i))
		{
			continue;
		}
		visible = qtrue;
		// get the ping for this server
		ping = trap_LAN_GetServerPing(ui_netSource.integer, i);
		if(ping > 0 || ui_netSource.integer == AS_FAVORITES)
		{

			trap_LAN_GetServerInfo(ui_netSource.integer, i, info, MAX_STRING_CHARS);

			clients = atoi(Info_ValueForKey(info, "clients"));
			uiInfo.serverStatus.numPlayersOnServers += clients;

			if(ui_browserShowEmpty.integer == 0)
			{
				if(clients == 0)
				{
					trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
					continue;
				}
			}

			if(ui_browserShowFull.integer == 0)
			{
				maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
				if(clients == maxClients)
				{
					trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
					continue;
				}
			}

			if(uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum != -1)
			{
				game = atoi(Info_ValueForKey(info, "gametype"));
				if(game != uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum)
				{
					trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
					continue;
				}
			}

			// make sure we never add a favorite server twice
			if(ui_netSource.integer == AS_FAVORITES)
			{
				UI_RemoveServerFromDisplayList(i);
			}
			// insert the server into the list
			UI_BinaryServerInsertion(i);
			// done with this server
			if(ping > 0)
			{
				trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
				numinvisible++;
			}
		}
	}

	uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime;

	// if there were no servers visible for ping updates
	if(!visible)
	{
//    UI_StopServerRefresh();
//    uiInfo.serverStatus.nextDisplayRefresh = 0;
	}
}

typedef struct
{
	char           *name, *altName;
} serverStatusCvar_t;

serverStatusCvar_t serverStatusCvars[] = {
	{"sv_hostname", "Name"},
	{"Address", ""},
	{"gamename", "Game name"},
	{"g_gametype", "Game type"},
	{"mapname", "Map"},
	{"version", ""},
	{"protocol", ""},
	{"timelimit", ""},
	{"fraglimit", ""},
	{NULL, NULL}
};

/*
==================
UI_SortServerStatusInfo
==================
*/
static void UI_SortServerStatusInfo(serverStatusInfo_t * info)
{
	int             i, j, index;
	char           *tmp1, *tmp2;

	// FIXME: if "gamename" == "baseq3" or "missionpack" then
	// replace the gametype number by FFA, CTF etc.
	//
	index = 0;
	for(i = 0; serverStatusCvars[i].name; i++)
	{
		for(j = 0; j < info->numLines; j++)
		{
			if(!info->lines[j][1] || info->lines[j][1][0])
			{
				continue;
			}
			if(!Q_stricmp(serverStatusCvars[i].name, info->lines[j][0]))
			{
				// swap lines
				tmp1 = info->lines[index][0];
				tmp2 = info->lines[index][3];
				info->lines[index][0] = info->lines[j][0];
				info->lines[index][3] = info->lines[j][3];
				info->lines[j][0] = tmp1;
				info->lines[j][3] = tmp2;
				//
				if(strlen(serverStatusCvars[i].altName))
				{
					info->lines[index][0] = serverStatusCvars[i].altName;
				}
				index++;
			}
		}
	}
}

/*
==================
UI_GetServerStatusInfo
==================
*/
static int UI_GetServerStatusInfo(const char *serverAddress, serverStatusInfo_t * info)
{
	char           *p, *score, *ping, *name;
	int             i, len;

	if(!info)
	{
		trap_LAN_ServerStatus(serverAddress, NULL, 0);
		return qfalse;
	}
	memset(info, 0, sizeof(*info));
	if(trap_LAN_ServerStatus(serverAddress, info->text, sizeof(info->text)))
	{
		Q_strncpyz(info->address, serverAddress, sizeof(info->address));
		p = info->text;
		info->numLines = 0;
		info->lines[info->numLines][0] = "Address";
		info->lines[info->numLines][1] = "";
		info->lines[info->numLines][2] = "";
		info->lines[info->numLines][3] = info->address;
		info->numLines++;
		// get the cvars
		while(p && *p)
		{
			p = strchr(p, '\\');
			if(!p)
				break;
			*p++ = '\0';
			if(*p == '\\')
				break;
			info->lines[info->numLines][0] = p;
			info->lines[info->numLines][1] = "";
			info->lines[info->numLines][2] = "";
			p = strchr(p, '\\');
			if(!p)
				break;
			*p++ = '\0';
			info->lines[info->numLines][3] = p;

			info->numLines++;
			if(info->numLines >= MAX_SERVERSTATUS_LINES)
				break;
		}
		// get the player list
		if(info->numLines < MAX_SERVERSTATUS_LINES - 3)
		{
			// empty line
			info->lines[info->numLines][0] = "";
			info->lines[info->numLines][1] = "";
			info->lines[info->numLines][2] = "";
			info->lines[info->numLines][3] = "";
			info->numLines++;
			// header
			info->lines[info->numLines][0] = "num";
			info->lines[info->numLines][1] = "score";
			info->lines[info->numLines][2] = "ping";
			info->lines[info->numLines][3] = "name";
			info->numLines++;
			// parse players
			i = 0;
			len = 0;
			while(p && *p)
			{
				if(*p == '\\')
					*p++ = '\0';
				if(!p)
					break;
				score = p;
				p = strchr(p, ' ');
				if(!p)
					break;
				*p++ = '\0';
				ping = p;
				p = strchr(p, ' ');
				if(!p)
					break;
				*p++ = '\0';
				name = p;
				Com_sprintf(&info->pings[len], sizeof(info->pings) - len, "%d", i);
				info->lines[info->numLines][0] = &info->pings[len];
				len += strlen(&info->pings[len]) + 1;
				info->lines[info->numLines][1] = score;
				info->lines[info->numLines][2] = ping;
				info->lines[info->numLines][3] = name;
				info->numLines++;
				if(info->numLines >= MAX_SERVERSTATUS_LINES)
					break;
				p = strchr(p, '\\');
				if(!p)
					break;
				*p++ = '\0';
				//
				i++;
			}
		}
		UI_SortServerStatusInfo(info);
		return qtrue;
	}
	return qfalse;
}

/*
==================
stristr
==================
*/
static char    *stristr(char *str, char *charset)
{
	int             i;

	while(*str)
	{
		for(i = 0; charset[i] && str[i]; i++)
		{
			if(toupper(charset[i]) != toupper(str[i]))
				break;
		}
		if(!charset[i])
			return str;
		str++;
	}
	return NULL;
}

/*
==================
UI_BuildFindPlayerList
==================
*/
static void UI_BuildFindPlayerList(qboolean force)
{
	static int      numFound, numTimeOuts;
	int             i, j, resend;
	serverStatusInfo_t info;
	char            name[MAX_NAME_LENGTH + 2];
	char            infoString[MAX_STRING_CHARS];

	if(!force)
	{
		if(!uiInfo.nextFindPlayerRefresh || uiInfo.nextFindPlayerRefresh > uiInfo.uiDC.realTime)
		{
			return;
		}
	}
	else
	{
		memset(&uiInfo.pendingServerStatus, 0, sizeof(uiInfo.pendingServerStatus));
		uiInfo.numFoundPlayerServers = 0;
		uiInfo.currentFoundPlayerServer = 0;
		trap_Cvar_VariableStringBuffer("ui_findPlayer", uiInfo.findPlayerName, sizeof(uiInfo.findPlayerName));
		Q_CleanStr(uiInfo.findPlayerName);
		// should have a string of some length
		if(!strlen(uiInfo.findPlayerName))
		{
			uiInfo.nextFindPlayerRefresh = 0;
			return;
		}
		// set resend time
		resend = ui_serverStatusTimeOut.integer / 2 - 10;
		if(resend < 50)
		{
			resend = 50;
		}
		trap_Cvar_Set("cl_serverStatusResendTime", va("%d", resend));
		// reset all server status requests
		trap_LAN_ServerStatus(NULL, NULL, 0);
		//
		uiInfo.numFoundPlayerServers = 1;
		Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1],
					sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1]),
					"searching %d...", uiInfo.pendingServerStatus.num);
		numFound = 0;
		numTimeOuts++;
	}
	for(i = 0; i < MAX_SERVERSTATUSREQUESTS; i++)
	{
		// if this pending server is valid
		if(uiInfo.pendingServerStatus.server[i].valid)
		{
			// try to get the server status for this server
			if(UI_GetServerStatusInfo(uiInfo.pendingServerStatus.server[i].adrstr, &info))
			{
				//
				numFound++;
				// parse through the server status lines
				for(j = 0; j < info.numLines; j++)
				{
					// should have ping info
					if(!info.lines[j][2] || !info.lines[j][2][0])
					{
						continue;
					}
					// clean string first
					Q_strncpyz(name, info.lines[j][3], sizeof(name));
					Q_CleanStr(name);
					// if the player name is a substring
					if(stristr(name, uiInfo.findPlayerName))
					{
						// add to found server list if we have space (always leave space for a line with the number found)
						if(uiInfo.numFoundPlayerServers < MAX_FOUNDPLAYER_SERVERS - 1)
						{
							//
							Q_strncpyz(uiInfo.foundPlayerServerAddresses[uiInfo.numFoundPlayerServers - 1],
									   uiInfo.pendingServerStatus.server[i].adrstr, sizeof(uiInfo.foundPlayerServerAddresses[0]));
							Q_strncpyz(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1],
									   uiInfo.pendingServerStatus.server[i].name, sizeof(uiInfo.foundPlayerServerNames[0]));
							uiInfo.numFoundPlayerServers++;
						}
						else
						{
							// can't add any more so we're done
							uiInfo.pendingServerStatus.num = uiInfo.serverStatus.numDisplayServers;
						}
					}
				}
				Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1],
							sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1]),
							"searching %d/%d...", uiInfo.pendingServerStatus.num, numFound);
				// retrieved the server status so reuse this spot
				uiInfo.pendingServerStatus.server[i].valid = qfalse;
			}
		}
		// if empty pending slot or timed out
		if(!uiInfo.pendingServerStatus.server[i].valid ||
		   uiInfo.pendingServerStatus.server[i].startTime < uiInfo.uiDC.realTime - ui_serverStatusTimeOut.integer)
		{
			if(uiInfo.pendingServerStatus.server[i].valid)
			{
				numTimeOuts++;
			}
			// reset server status request for this address
			UI_GetServerStatusInfo(uiInfo.pendingServerStatus.server[i].adrstr, NULL);
			// reuse pending slot
			uiInfo.pendingServerStatus.server[i].valid = qfalse;
			// if we didn't try to get the status of all servers in the main browser yet
			if(uiInfo.pendingServerStatus.num < uiInfo.serverStatus.numDisplayServers)
			{
				uiInfo.pendingServerStatus.server[i].startTime = uiInfo.uiDC.realTime;
				trap_LAN_GetServerAddressString(ui_netSource.integer,
												uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num],
												uiInfo.pendingServerStatus.server[i].adrstr,
												sizeof(uiInfo.pendingServerStatus.server[i].adrstr));
				trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num],
									   infoString, sizeof(infoString));
				Q_strncpyz(uiInfo.pendingServerStatus.server[i].name, Info_ValueForKey(infoString, "hostname"),
						   sizeof(uiInfo.pendingServerStatus.server[0].name));
				uiInfo.pendingServerStatus.server[i].valid = qtrue;
				uiInfo.pendingServerStatus.num++;
				Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1],
							sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1]),
							"searching %d/%d...", uiInfo.pendingServerStatus.num, numFound);
			}
		}
	}
	for(i = 0; i < MAX_SERVERSTATUSREQUESTS; i++)
	{
		if(uiInfo.pendingServerStatus.server[i].valid)
		{
			break;
		}
	}
	// if still trying to retrieve server status info
	if(i < MAX_SERVERSTATUSREQUESTS)
	{
		uiInfo.nextFindPlayerRefresh = uiInfo.uiDC.realTime + 25;
	}
	else
	{
		// add a line that shows the number of servers found
		if(!uiInfo.numFoundPlayerServers)
		{
			Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1],
						sizeof(uiInfo.foundPlayerServerAddresses[0]), "no servers found");
		}
		else
		{
			Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers - 1],
						sizeof(uiInfo.foundPlayerServerAddresses[0]), "%d server%s found with player %s",
						uiInfo.numFoundPlayerServers - 1, uiInfo.numFoundPlayerServers == 2 ? "" : "s", uiInfo.findPlayerName);
		}
		uiInfo.nextFindPlayerRefresh = 0;
		// show the server status info for the selected server
		UI_FeederSelection(FEEDER_FINDPLAYER, uiInfo.currentFoundPlayerServer);
	}
}

/*
==================
UI_BuildServerStatus
==================
*/
static void UI_BuildServerStatus(qboolean force)
{

	if(uiInfo.nextFindPlayerRefresh)
	{
		return;
	}
	if(!force)
	{
		if(!uiInfo.nextServerStatusRefresh || uiInfo.nextServerStatusRefresh > uiInfo.uiDC.realTime)
		{
			return;
		}
	}
	else
	{
		Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL);
		uiInfo.serverStatusInfo.numLines = 0;
		// reset all server status requests
		trap_LAN_ServerStatus(NULL, NULL, 0);
	}
	if(uiInfo.serverStatus.currentServer < 0 || uiInfo.serverStatus.currentServer > uiInfo.serverStatus.numDisplayServers ||
	   uiInfo.serverStatus.numDisplayServers == 0)
	{
		return;
	}
	if(UI_GetServerStatusInfo(uiInfo.serverStatusAddress, &uiInfo.serverStatusInfo))
	{
		uiInfo.nextServerStatusRefresh = 0;
		UI_GetServerStatusInfo(uiInfo.serverStatusAddress, NULL);
	}
	else
	{
		uiInfo.nextServerStatusRefresh = uiInfo.uiDC.realTime + 500;
	}
}

/*
==================
UI_FeederCount
==================
*/
static int UI_FeederCount(float feederID)
{
	if(feederID == FEEDER_HEADS)
	{
		return UI_HeadCountByTeam();
	}
	else if(feederID == FEEDER_Q3HEADS)
	{
		return uiInfo.q3HeadCount;
	}
	else if(feederID == FEEDER_CINEMATICS)
	{
		return uiInfo.movieCount;
	}
	else if(feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS)
	{
		return UI_MapCountByGameType(feederID == FEEDER_MAPS ? qtrue : qfalse);
	}
	else if(feederID == FEEDER_SERVERS)
	{
		return uiInfo.serverStatus.numDisplayServers;
	}
	else if(feederID == FEEDER_SERVERSTATUS)
	{
		return uiInfo.serverStatusInfo.numLines;
	}
	else if(feederID == FEEDER_FINDPLAYER)
	{
		return uiInfo.numFoundPlayerServers;
	}
	else if(feederID == FEEDER_PLAYER_LIST)
	{
		if(uiInfo.uiDC.realTime > uiInfo.playerRefresh)
		{
			uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
			UI_BuildPlayerList();
		}
		return uiInfo.playerCount;
	}
	else if(feederID == FEEDER_TEAM_LIST)
	{
		if(uiInfo.uiDC.realTime > uiInfo.playerRefresh)
		{
			uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
			UI_BuildPlayerList();
		}
		return uiInfo.myTeamCount;
	}
	else if(feederID == FEEDER_MODS)
	{
		return uiInfo.modCount;
	}
	else if(feederID == FEEDER_DEMOS)
	{
		return uiInfo.demoCount;
	}

//TA: tremulous menus
	else if(feederID == FEEDER_TREMTEAMS)
		return uiInfo.tremTeamCount;
	else if(feederID == FEEDER_TREMHUMANITEMS)
		return uiInfo.tremHumanItemCount;
	else if(feederID == FEEDER_TREMALIENCLASSES)
		return uiInfo.tremAlienClassCount;
	else if(feederID == FEEDER_TREMHUMANARMOURYBUY)
		return uiInfo.tremHumanArmouryBuyCount;
	else if(feederID == FEEDER_TREMHUMANARMOURYSELL)
		return uiInfo.tremHumanArmourySellCount;
	else if(feederID == FEEDER_TREMALIENUPGRADE)
		return uiInfo.tremAlienUpgradeCount;
	else if(feederID == FEEDER_TREMALIENBUILD)
		return uiInfo.tremAlienBuildCount;
	else if(feederID == FEEDER_TREMHUMANBUILD)
		return uiInfo.tremHumanBuildCount;
//TA: tremulous menus
	else if(feederID == FEEDER_CREDITS)
			return numCredits;
	return 0;
}

static const char *UI_SelectedMap(int index, int *actual)
{
	int             i, c;

	c = 0;
	*actual = 0;
	for(i = 0; i < uiInfo.mapCount; i++)
	{
		if(uiInfo.mapList[i].active)
		{
			if(c == index)
			{
				*actual = i;
				return uiInfo.mapList[i].mapName;
			}
			else
			{
				c++;
			}
		}
	}
	return "";
}

static const char *UI_SelectedHead(int index, int *actual)
{
	int             i, c;

	c = 0;
	*actual = 0;
	for(i = 0; i < uiInfo.characterCount; i++)
	{
		if(uiInfo.characterList[i].active)
		{
			if(c == index)
			{
				*actual = i;
				return uiInfo.characterList[i].name;
			}
			else
			{
				c++;
			}
		}
	}
	return "";
}

static int UI_GetIndexFromSelection(int actual)
{
	int             i, c;

	c = 0;
	for(i = 0; i < uiInfo.mapCount; i++)
	{
		if(uiInfo.mapList[i].active)
		{
			if(i == actual)
			{
				return c;
			}
			c++;
		}
	}
	return 0;
}

static void UI_UpdatePendingPings(void)
{
	trap_LAN_ResetPings(ui_netSource.integer);
	uiInfo.serverStatus.refreshActive = qtrue;
	uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;

}

static const char *UI_FeederItemText(float feederID, int index, int column, qhandle_t * handle)
{
	static char     info[MAX_STRING_CHARS];
	static char     hostname[1024];
	static char     clientBuff[32];
	static int      lastColumn = -1;
	static int      lastTime = 0;

	*handle = -1;
	if(feederID == FEEDER_HEADS)
	{
		int             actual;

		return UI_SelectedHead(index, &actual);
	}
	else if(feederID == FEEDER_Q3HEADS)
	{
		if(index >= 0 && index < uiInfo.q3HeadCount)
		{
			return uiInfo.q3HeadNames[index];
		}
	}
	else if(feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS)
	{
		int             actual;

		return UI_SelectedMap(index, &actual);
	}
	else if(feederID == FEEDER_SERVERS)
	{
		if(index >= 0 && index < uiInfo.serverStatus.numDisplayServers)
		{
			int             ping;

			if(lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000)
			{
				trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS);
				lastColumn = column;
				lastTime = uiInfo.uiDC.realTime;
			}

			ping = atoi(Info_ValueForKey(info, "ping"));
			if(ping == -1)
			{
				// if we ever see a ping that is out of date, do a server refresh
				// UI_UpdatePendingPings();
			}
			switch (column)
			{
				case SORT_HOST:
					if(ping <= 0)
					{
						return Info_ValueForKey(info, "addr");
					}
					else
					{
						if(ui_netSource.integer == AS_LOCAL)
						{
							Com_sprintf(hostname, sizeof(hostname), "%s [%s]",
										Info_ValueForKey(info, "hostname"), netnames[atoi(Info_ValueForKey(info, "nettype"))]);
							return hostname;
						}
						else
						{
							Com_sprintf(hostname, sizeof(hostname), "%s", Info_ValueForKey(info, "hostname"));

							return hostname;
						}
					}
				case SORT_MAP:
					return Info_ValueForKey(info, "mapname");
				case SORT_CLIENTS:
					Com_sprintf(clientBuff, sizeof(clientBuff), "%s (%s)", Info_ValueForKey(info, "clients"),
								Info_ValueForKey(info, "sv_maxclients"));
					return clientBuff;
				case SORT_PING:
					if(ping <= 0)
					{
						return "...";
					}
					else
					{
						return Info_ValueForKey(info, "ping");
					}
			}
		}
	}
	else if(feederID == FEEDER_SERVERSTATUS)
	{
		if(index >= 0 && index < uiInfo.serverStatusInfo.numLines)
		{
			if(column >= 0 && column < 4)
			{
				return uiInfo.serverStatusInfo.lines[index][column];
			}
		}
	}
	else if(feederID == FEEDER_FINDPLAYER)
	{
		if(index >= 0 && index < uiInfo.numFoundPlayerServers)
		{
			//return uiInfo.foundPlayerServerAddresses[index];
			return uiInfo.foundPlayerServerNames[index];
		}
	}
	else if(feederID == FEEDER_PLAYER_LIST)
	{
		if(index >= 0 && index < uiInfo.playerCount)
		{
			return uiInfo.playerNames[index];
		}
	}
	else if(feederID == FEEDER_TEAM_LIST)
	{
		if(index >= 0 && index < uiInfo.myTeamCount)
		{
			return uiInfo.teamNames[index];
		}
	}
	else if(feederID == FEEDER_MODS)
	{
		if(index >= 0 && index < uiInfo.modCount)
		{
			if(uiInfo.modList[index].modDescr && *uiInfo.modList[index].modDescr)
			{
				return uiInfo.modList[index].modDescr;
			}
			else
			{
				return uiInfo.modList[index].modName;
			}
		}
	}
	else if(feederID == FEEDER_CINEMATICS)
	{
		if(index >= 0 && index < uiInfo.movieCount)
		{
			return uiInfo.movieList[index];
		}
	}
	else if(feederID == FEEDER_DEMOS)
	{
		if(index >= 0 && index < uiInfo.demoCount)
		{
			return uiInfo.demoList[index];
		}
	}

//TA: tremulous menus
	else if(feederID == FEEDER_TREMTEAMS)
	{
		if(index >= 0 && index < uiInfo.tremTeamCount)
			return uiInfo.tremTeamList[index].text;
	}
	else if(feederID == FEEDER_TREMHUMANITEMS)
	{
		if(index >= 0 && index < uiInfo.tremHumanItemCount)
			return uiInfo.tremHumanItemList[index].text;
	}
	else if(feederID == FEEDER_TREMALIENCLASSES)
	{
		if(index >= 0 && index < uiInfo.tremAlienClassCount)
			return uiInfo.tremAlienClassList[index].text;
	}
	else if(feederID == FEEDER_TREMHUMANARMOURYBUY)
	{
		if(index >= 0 && index < uiInfo.tremHumanArmouryBuyCount)
			return uiInfo.tremHumanArmouryBuyList[index].text;
	}
	else if(feederID == FEEDER_TREMHUMANARMOURYSELL)
	{
		if(index >= 0 && index < uiInfo.tremHumanArmourySellCount)
			return uiInfo.tremHumanArmourySellList[index].text;
	}
	else if(feederID == FEEDER_TREMALIENUPGRADE)
	{
		if(index >= 0 && index < uiInfo.tremAlienUpgradeCount)
			return uiInfo.tremAlienUpgradeList[index].text;
	}
	else if(feederID == FEEDER_TREMALIENBUILD)
	{
		if(index >= 0 && index < uiInfo.tremAlienBuildCount)
			return uiInfo.tremAlienBuildList[index].text;
	}
	else if(feederID == FEEDER_TREMHUMANBUILD)
	{
		if(index >= 0 && index < uiInfo.tremHumanBuildCount)
			return uiInfo.tremHumanBuildList[index].text;
	}
//TA: tremulous menus
	else if(feederID == FEEDER_CREDITS)
	{
		if(index >= 0 && index < numCredits)
			switch(column)
			{
				case 0:
					return credits[index].name;
				case 1:
					return credits[index].location;
				case 2:
					return credits[index].component;
				case 3:
					return credits[index].tasks;
				default:
					return "";
			}
	}
	return "";
}


static qhandle_t UI_FeederItemImage(float feederID, int index)
{
	if(feederID == FEEDER_HEADS)
	{
		int             actual;

		UI_SelectedHead(index, &actual);
		index = actual;
		if(index >= 0 && index < uiInfo.characterCount)
		{
			if(uiInfo.characterList[index].headImage == -1)
			{
				uiInfo.characterList[index].headImage = trap_R_RegisterShaderNoMip(uiInfo.characterList[index].imageName);
			}
			return uiInfo.characterList[index].headImage;
		}
	}
	else if(feederID == FEEDER_Q3HEADS)
	{
		if(index >= 0 && index < uiInfo.q3HeadCount)
		{
			return uiInfo.q3HeadIcons[index];
		}
	}
	else if(feederID == FEEDER_ALLMAPS || feederID == FEEDER_MAPS)
	{
		int             actual;

		UI_SelectedMap(index, &actual);
		index = actual;
		if(index >= 0 && index < uiInfo.mapCount)
		{
			if(uiInfo.mapList[index].levelShot == -1)
			{
				uiInfo.mapList[index].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[index].imageName);
			}
			return uiInfo.mapList[index].levelShot;
		}
	}
	return 0;
}

static void UI_FeederSelection(float feederID, int index)
{
	static char     info[MAX_STRING_CHARS];

	if(feederID == FEEDER_HEADS)
	{
		int             actual;

		UI_SelectedHead(index, &actual);
		index = actual;
		if(index >= 0 && index < uiInfo.characterCount)
		{
			trap_Cvar_Set("team_model", va("%s", uiInfo.characterList[index].base));
			trap_Cvar_Set("team_headmodel", va("*%s", uiInfo.characterList[index].name));
			updateModel = qtrue;
		}
	}
	else if(feederID == FEEDER_Q3HEADS)
	{
		if(index >= 0 && index < uiInfo.q3HeadCount)
		{
			trap_Cvar_Set("model", uiInfo.q3HeadNames[index]);
			trap_Cvar_Set("headmodel", uiInfo.q3HeadNames[index]);
			updateModel = qtrue;
		}
	}
	else if(feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS)
	{
		int             actual, map;

		map = (feederID == FEEDER_ALLMAPS) ? ui_currentNetMap.integer : ui_currentMap.integer;
		if(uiInfo.mapList[map].cinematic >= 0)
		{
			trap_CIN_StopCinematic(uiInfo.mapList[map].cinematic);
			uiInfo.mapList[map].cinematic = -1;
		}
		UI_SelectedMap(index, &actual);
		trap_Cvar_Set("ui_mapIndex", va("%d", index));
		ui_mapIndex.integer = index;

		if(feederID == FEEDER_MAPS)
		{
			ui_currentMap.integer = actual;
			trap_Cvar_Set("ui_currentMap", va("%d", actual));
			uiInfo.mapList[ui_currentMap.integer].cinematic =
				trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentMap.integer].mapLoadName), 0, 0, 0, 0,
									   (CIN_loop | CIN_silent));
			UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);
			trap_Cvar_Set("ui_opponentModel", uiInfo.mapList[ui_currentMap.integer].opponentName);
			updateOpponentModel = qtrue;
		}
		else
		{
			ui_currentNetMap.integer = actual;
			trap_Cvar_Set("ui_currentNetMap", va("%d", actual));
			uiInfo.mapList[ui_currentNetMap.integer].cinematic =
				trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName), 0, 0, 0, 0,
									   (CIN_loop | CIN_silent));
		}

	}
	else if(feederID == FEEDER_SERVERS)
	{
		const char     *mapName = NULL;

		uiInfo.serverStatus.currentServer = index;
		trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS);
		uiInfo.serverStatus.currentServerPreview =
			trap_R_RegisterShaderNoMip(va("levelshots/%s", Info_ValueForKey(info, "mapname")));
		if(uiInfo.serverStatus.currentServerCinematic >= 0)
		{
			trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic);
			uiInfo.serverStatus.currentServerCinematic = -1;
		}
		mapName = Info_ValueForKey(info, "mapname");
		if(mapName && *mapName)
		{
			uiInfo.serverStatus.currentServerCinematic =
				trap_CIN_PlayCinematic(va("%s.roq", mapName), 0, 0, 0, 0, (CIN_loop | CIN_silent));
		}
	}
	else if(feederID == FEEDER_SERVERSTATUS)
	{
		//
	}
	else if(feederID == FEEDER_FINDPLAYER)
	{
		uiInfo.currentFoundPlayerServer = index;
		//
		if(index < uiInfo.numFoundPlayerServers - 1)
		{
			// build a new server status for this server
			Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer],
					   sizeof(uiInfo.serverStatusAddress));
			Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL);
			UI_BuildServerStatus(qtrue);
		}
	}
	else if(feederID == FEEDER_PLAYER_LIST)
	{
		uiInfo.playerIndex = index;
	}
	else if(feederID == FEEDER_TEAM_LIST)
	{
		uiInfo.teamIndex = index;
	}
	else if(feederID == FEEDER_MODS)
	{
		uiInfo.modIndex = index;
	}
	else if(feederID == FEEDER_CINEMATICS)
	{
		uiInfo.movieIndex = index;
		if(uiInfo.previewMovie >= 0)
		{
			trap_CIN_StopCinematic(uiInfo.previewMovie);
		}
		uiInfo.previewMovie = -1;
	}
	else if(feederID == FEEDER_DEMOS)
	{
		uiInfo.demoIndex = index;
	}

//TA: tremulous menus
	else if(feederID == FEEDER_TREMTEAMS)
		uiInfo.tremTeamIndex = index;
	else if(feederID == FEEDER_TREMHUMANITEMS)
		uiInfo.tremHumanItemIndex = index;
	else if(feederID == FEEDER_TREMALIENCLASSES)
		uiInfo.tremAlienClassIndex = index;
	else if(feederID == FEEDER_TREMHUMANARMOURYBUY)
		uiInfo.tremHumanArmouryBuyIndex = index;
	else if(feederID == FEEDER_TREMHUMANARMOURYSELL)
		uiInfo.tremHumanArmourySellIndex = index;
	else if(feederID == FEEDER_TREMALIENUPGRADE)
		uiInfo.tremAlienUpgradeIndex = index;
	else if(feederID == FEEDER_TREMALIENBUILD)
		uiInfo.tremAlienBuildIndex = index;
	else if(feederID == FEEDER_TREMHUMANBUILD)
		uiInfo.tremHumanBuildIndex = index;
//TA: tremulous menus
}

static void UI_Pause(qboolean b)
{
	if(b)
	{
		// pause the game and set the ui keycatcher
		trap_Cvar_Set("cl_paused", "1");
		trap_Key_SetCatcher(KEYCATCH_UI);
	}
	else
	{
		// unpause the game and clear the ui keycatcher
		trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
		trap_Key_ClearStates();
		trap_Cvar_Set("cl_paused", "0");
	}
}

static int UI_PlayCinematic(const char *name, float x, float y, float w, float h)
{
	return trap_CIN_PlayCinematic(name, x, y, w, h, (CIN_loop | CIN_silent));
}

static void UI_StopCinematic(int handle)
{
	if(handle >= 0)
	{
		trap_CIN_StopCinematic(handle);
	}
	else
	{
		handle = abs(handle);
		if(handle == UI_MAPCINEMATIC)
		{
			if(uiInfo.mapList[ui_currentMap.integer].cinematic >= 0)
			{
				trap_CIN_StopCinematic(uiInfo.mapList[ui_currentMap.integer].cinematic);
				uiInfo.mapList[ui_currentMap.integer].cinematic = -1;
			}
		}
		else if(handle == UI_NETMAPCINEMATIC)
		{
			if(uiInfo.serverStatus.currentServerCinematic >= 0)
			{
				trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic);
				uiInfo.serverStatus.currentServerCinematic = -1;
			}
		}
		else if(handle == UI_CLANCINEMATIC)
		{
			int             i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));

			if(i >= 0 && i < uiInfo.teamCount)
			{
				if(uiInfo.teamList[i].cinematic >= 0)
				{
					trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic);
					uiInfo.teamList[i].cinematic = -1;
				}
			}
		}
	}
}

static void UI_DrawCinematic(int handle, float x, float y, float w, float h)
{
	trap_CIN_SetExtents(handle, x, y, w, h);
	trap_CIN_DrawCinematic(handle);
}

static void UI_RunCinematicFrame(int handle)
{
	trap_CIN_RunCinematic(handle);
}



/*
=================
PlayerModel_BuildList
=================
*/
static void UI_BuildQ3Model_List(void)
{
	int             numdirs;
	int             numfiles;
	char            dirlist[2048];
	char            filelist[2048];
	char            skinname[64];
	char            scratch[256];
	char           *dirptr;
	char           *fileptr;
	int             i;
	int             j, k, dirty;
	int             dirlen;
	int             filelen;

	uiInfo.q3HeadCount = 0;

	// iterate directory of all player models
	numdirs = trap_FS_GetFileList("models/players", "/", dirlist, 2048);
	dirptr = dirlist;
	for(i = 0; i < numdirs && uiInfo.q3HeadCount < MAX_PLAYERMODELS; i++, dirptr += dirlen + 1)
	{
		dirlen = strlen(dirptr);

		if(dirlen && dirptr[dirlen - 1] == '/')
			dirptr[dirlen - 1] = '\0';

		if(!strcmp(dirptr, ".") || !strcmp(dirptr, ".."))
			continue;

		// iterate all skin files in directory
		numfiles = trap_FS_GetFileList(va("models/players/%s", dirptr), "tga", filelist, 2048);
		fileptr = filelist;
		for(j = 0; j < numfiles && uiInfo.q3HeadCount < MAX_PLAYERMODELS; j++, fileptr += filelen + 1)
		{
			filelen = strlen(fileptr);

			Com_StripExtension(fileptr, skinname, sizeof(skinname));

			// look for icon_????
			if(Q_stricmpn(skinname, "icon_", 5) == 0 &&
			   !(Q_stricmp(skinname, "icon_blue") == 0 || Q_stricmp(skinname, "icon_red") == 0))
			{
				if(Q_stricmp(skinname, "icon_default") == 0)
				{
					Com_sprintf(scratch, sizeof(scratch), dirptr);
				}
				else
				{
					Com_sprintf(scratch, sizeof(scratch), "%s/%s", dirptr, skinname + 5);
				}
				dirty = 0;
				for(k = 0; k < uiInfo.q3HeadCount; k++)
				{
					if(!Q_stricmp(scratch, uiInfo.q3HeadNames[uiInfo.q3HeadCount]))
					{
						dirty = 1;
						break;
					}
				}
				if(!dirty)
				{
					Com_sprintf(uiInfo.q3HeadNames[uiInfo.q3HeadCount], sizeof(uiInfo.q3HeadNames[uiInfo.q3HeadCount]), scratch);
					uiInfo.q3HeadIcons[uiInfo.q3HeadCount++] =
						trap_R_RegisterShaderNoMip(va("models/players/%s/%s", dirptr, skinname));
				}
			}

		}
	}

}



/*
=================
UI_Init
=================
*/
void _UI_Init(qboolean inGameLoad)
{
	const char     *menuSet;
	int             start;

	BG_InitClassOverrides();
	BG_InitAllowedGameElements();

	//uiInfo.inGameLoad = inGameLoad;

	UI_RegisterCvars();
	UI_InitMemory();

	// cache redundant calulations
	trap_GetGlconfig(&uiInfo.uiDC.glconfig);

	// for 640x480 virtualized screen
	uiInfo.uiDC.yscale = uiInfo.uiDC.glconfig.vidHeight * (1.0 / 480.0);
	uiInfo.uiDC.xscale = uiInfo.uiDC.glconfig.vidWidth * (1.0 / 640.0);
	if(uiInfo.uiDC.glconfig.vidWidth * 480 > uiInfo.uiDC.glconfig.vidHeight * 640)
	{
		// wide screen
		uiInfo.uiDC.bias = 0.5 * (uiInfo.uiDC.glconfig.vidWidth - (uiInfo.uiDC.glconfig.vidHeight * (640.0 / 480.0)));
	}
	else
	{
		// no wide screen
		uiInfo.uiDC.bias = 0;
	}


	//UI_Load();
	uiInfo.uiDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip;
	uiInfo.uiDC.setColor = &UI_SetColor;
	uiInfo.uiDC.drawHandlePic = &UI_DrawHandlePic;
	uiInfo.uiDC.drawStretchPic = &trap_R_DrawStretchPic;
	uiInfo.uiDC.drawText = &Text_Paint;
	uiInfo.uiDC.textWidth = &Text_Width;
	uiInfo.uiDC.textHeight = &Text_Height;
	uiInfo.uiDC.registerModel = &trap_R_RegisterModel;
	uiInfo.uiDC.modelBounds = &trap_R_ModelBounds;
	uiInfo.uiDC.fillRect = &UI_FillRect;
	uiInfo.uiDC.drawRect = &_UI_DrawRect;
	uiInfo.uiDC.drawSides = &_UI_DrawSides;
	uiInfo.uiDC.drawTopBottom = &_UI_DrawTopBottom;
	uiInfo.uiDC.clearScene = &trap_R_ClearScene;
	uiInfo.uiDC.drawSides = &_UI_DrawSides;
	uiInfo.uiDC.addRefEntityToScene = &trap_R_AddRefEntityToScene;
	uiInfo.uiDC.renderScene = &trap_R_RenderScene;
	uiInfo.uiDC.registerFont = &trap_R_RegisterFont;
	uiInfo.uiDC.ownerDrawItem = &UI_OwnerDraw;
	uiInfo.uiDC.getValue = &UI_GetValue;
	uiInfo.uiDC.ownerDrawVisible = &UI_OwnerDrawVisible;
	uiInfo.uiDC.runScript = &UI_RunMenuScript;
	uiInfo.uiDC.getTeamColor = &UI_GetTeamColor;
	uiInfo.uiDC.setCVar = trap_Cvar_Set;
	uiInfo.uiDC.getCVarString = trap_Cvar_VariableStringBuffer;
	uiInfo.uiDC.getCVarValue = trap_Cvar_VariableValue;
	uiInfo.uiDC.drawTextWithCursor = &Text_PaintWithCursor;
	uiInfo.uiDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode;
	uiInfo.uiDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode;
	uiInfo.uiDC.startLocalSound = &trap_S_StartLocalSound;
	uiInfo.uiDC.ownerDrawHandleKey = &UI_OwnerDrawHandleKey;
	uiInfo.uiDC.feederCount = &UI_FeederCount;
	uiInfo.uiDC.feederItemImage = &UI_FeederItemImage;
	uiInfo.uiDC.feederItemText = &UI_FeederItemText;
	uiInfo.uiDC.feederSelection = &UI_FeederSelection;
	uiInfo.uiDC.setBinding = &trap_Key_SetBinding;
	uiInfo.uiDC.getBindingBuf = &trap_Key_GetBindingBuf;
	uiInfo.uiDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf;
	uiInfo.uiDC.executeText = &trap_Cmd_ExecuteText;
	uiInfo.uiDC.Error = &Com_Error;
	uiInfo.uiDC.Print = &Com_Printf;
	uiInfo.uiDC.Pause = &UI_Pause;
	uiInfo.uiDC.ownerDrawWidth = &UI_OwnerDrawWidth;
	uiInfo.uiDC.registerSound = &trap_S_RegisterSound;
	uiInfo.uiDC.startBackgroundTrack = &trap_S_StartBackgroundTrack;
	uiInfo.uiDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack;
	uiInfo.uiDC.playCinematic = &UI_PlayCinematic;
	uiInfo.uiDC.stopCinematic = &UI_StopCinematic;
	uiInfo.uiDC.drawCinematic = &UI_DrawCinematic;
	uiInfo.uiDC.runCinematicFrame = &UI_RunCinematicFrame;

	Init_Display(&uiInfo.uiDC);

	String_Init();

	uiInfo.uiDC.whiteShader = trap_R_RegisterShaderNoMip("white");

	AssetCache();

	start = trap_Milliseconds();

	uiInfo.teamCount = 0;
	uiInfo.characterCount = 0;
	uiInfo.aliasCount = 0;

/*  UI_ParseTeamInfo("teaminfo.txt");
  UI_LoadTeams();
  UI_ParseGameInfo("gameinfo.txt");*/

	menuSet = UI_Cvar_VariableString("ui_menuFiles");
	if(menuSet == NULL || menuSet[0] == '\0')
	{
		menuSet = "ui/menus.txt";
	}

#if 0
	if(uiInfo.inGameLoad)
	{
		UI_LoadMenus("ui/ingame.txt", qtrue);
	}
	else
	{							// bk010222: left this: UI_LoadMenus(menuSet, qtrue);
	}
#else
	UI_LoadMenus(menuSet, qtrue);
	UI_LoadMenus("ui/ingame.txt", qfalse);
	UI_LoadMenus("ui/tremulous.txt", qfalse);

	UI_LoadInfoPanes("ui/infopanes.def");

	if(uiInfo.uiDC.debug)
	{
		int             i, j;

		for(i = 0; i < uiInfo.tremInfoPaneCount; i++)
		{
			Com_Printf("name: %s\n", uiInfo.tremInfoPanes[i].name);

			Com_Printf("text: %s\n", uiInfo.tremInfoPanes[i].text);

			for(j = 0; j < uiInfo.tremInfoPanes[i].numGraphics; j++)
				Com_Printf("graphic %d: %d %d %d %d\n", j, uiInfo.tremInfoPanes[i].graphics[j].side,
						   uiInfo.tremInfoPanes[i].graphics[j].offset,
						   uiInfo.tremInfoPanes[i].graphics[j].width, uiInfo.tremInfoPanes[i].graphics[j].height);
		}
	}
#endif

	Menus_CloseAll();

	trap_LAN_LoadCachedServers();
	UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);

	UI_BuildQ3Model_List();
	/*UI_LoadBots(); */

	// sets defaults for ui temp cvars
	uiInfo.effectsColor = gamecodetoui[(int)trap_Cvar_VariableValue("color1") - 1];
	uiInfo.currentCrosshair = (int)trap_Cvar_VariableValue("cg_drawCrosshair");
	trap_Cvar_Set("ui_mousePitch", (trap_Cvar_VariableValue("m_pitch") >= 0) ? "0" : "1");

	uiInfo.serverStatus.currentServerCinematic = -1;
	uiInfo.previewMovie = -1;

	if(trap_Cvar_VariableValue("ui_TeamArenaFirstRun") == 0)
	{
		trap_Cvar_Set("s_volume", "0.8");
		trap_Cvar_Set("s_musicvolume", "0.5");
		trap_Cvar_Set("ui_TeamArenaFirstRun", "1");
	}

	trap_Cvar_Register(NULL, "debug_protocol", "", 0);

	trap_Cvar_Set("ui_actualNetGameType", va("%d", ui_netGameType.integer));
}


/*
=================
UI_KeyEvent
=================
*/
void _UI_KeyEvent(int key, qboolean down)
{

	if(Menu_Count() > 0)
	{
		menuDef_t      *menu = Menu_GetFocused();

		if(menu)
		{
			if(key == K_ESCAPE && down && !Menus_AnyFullScreenVisible())
			{
				Menus_CloseAll();
			}
			else
			{
				Menu_HandleKey(menu, key, down);
			}
		}
		else
		{
			trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
			trap_Key_ClearStates();
			trap_Cvar_Set("cl_paused", "0");
		}
	}

	//if ((s > 0) && (s != menu_null_sound)) {
	//  trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND );
	//}
}

/*
=================
UI_MouseEvent
=================
*/
void _UI_MouseEvent(int dx, int dy)
{
	// update mouse screen position
	uiInfo.uiDC.cursorx += dx;
	if(uiInfo.uiDC.cursorx < 0)
		uiInfo.uiDC.cursorx = 0;
	else if(uiInfo.uiDC.cursorx > SCREEN_WIDTH)
		uiInfo.uiDC.cursorx = SCREEN_WIDTH;

	uiInfo.uiDC.cursory += dy;
	if(uiInfo.uiDC.cursory < 0)
		uiInfo.uiDC.cursory = 0;
	else if(uiInfo.uiDC.cursory > SCREEN_HEIGHT)
		uiInfo.uiDC.cursory = SCREEN_HEIGHT;

	if(Menu_Count() > 0)
	{
		//menuDef_t *menu = Menu_GetFocused();
		//Menu_HandleMouseMove(menu, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory);
		Display_MouseMove(NULL, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory);
	}

}

void UI_LoadNonIngame(void)
{
	const char     *menuSet = UI_Cvar_VariableString("ui_menuFiles");

	if(menuSet == NULL || menuSet[0] == '\0')
	{
		menuSet = "ui/menus.txt";
	}
	UI_LoadMenus(menuSet, qfalse);
	uiInfo.inGameLoad = qfalse;
}

void _UI_SetActiveMenu(uiMenuCommand_t menu)
{
	char            buf[256];

	// this should be the ONLY way the menu system is brought up
	// enusure minumum menu data is cached
	if(Menu_Count() > 0)
	{
		vec3_t          v;

		v[0] = v[1] = v[2] = 0;
		switch (menu)
		{
			case UIMENU_NONE:
				trap_Key_SetCatcher(trap_Key_GetCatcher() & ~KEYCATCH_UI);
				trap_Key_ClearStates();
				trap_Cvar_Set("cl_paused", "0");
				Menus_CloseAll();

				return;
			case UIMENU_MAIN:
				//trap_Cvar_Set( "sv_killserver", "1" );
				trap_Key_SetCatcher(KEYCATCH_UI);
				//trap_S_StartLocalSound( trap_S_RegisterSound("sound/misc/menu_background.wav", qfalse) , CHAN_LOCAL_SOUND );
				//trap_S_StartBackgroundTrack("sound/misc/menu_background.wav", NULL);
				if(uiInfo.inGameLoad)
				{
					UI_LoadNonIngame();
				}
				Menus_CloseAll();
				Menus_ActivateByName("main");
				trap_Cvar_VariableStringBuffer("com_errorMessage", buf, sizeof(buf));
				if(strlen(buf))
				{
					if(!ui_singlePlayerActive.integer)
					{
						Menus_ActivateByName("error_popmenu");
					}
					else
					{
						trap_Cvar_Set("com_errorMessage", "");
					}
				}
				return;
			case UIMENU_TEAM:
				trap_Key_SetCatcher(KEYCATCH_UI);
				Menus_ActivateByName("team");
				return;
			case UIMENU_POSTGAME:
				//trap_Cvar_Set( "sv_killserver", "1" );
				trap_Key_SetCatcher(KEYCATCH_UI);
				if(uiInfo.inGameLoad)
				{
					UI_LoadNonIngame();
				}
				Menus_CloseAll();
				Menus_ActivateByName("endofgame");
				return;
			case UIMENU_INGAME:
				trap_Cvar_Set("cl_paused", "1");
				trap_Key_SetCatcher(KEYCATCH_UI);
				UI_BuildPlayerList();
				Menus_CloseAll();
				Menus_ActivateByName("ingame");
				return;
		}
	}
}

qboolean _UI_IsFullscreen(void)
{
	return Menus_AnyFullScreenVisible();
}



static connstate_t lastConnState;
static char     lastLoadingText[MAX_INFO_VALUE];

static void UI_ReadableSize(char *buf, int bufsize, int value)
{
	if(value > 1024 * 1024 * 1024)
	{							// gigs
		Com_sprintf(buf, bufsize, "%d", value / (1024 * 1024 * 1024));
		Com_sprintf(buf + strlen(buf), bufsize - strlen(buf), ".%02d GB",
					(value % (1024 * 1024 * 1024)) * 100 / (1024 * 1024 * 1024));
	}
	else if(value > 1024 * 1024)
	{							// megs
		Com_sprintf(buf, bufsize, "%d", value / (1024 * 1024));
		Com_sprintf(buf + strlen(buf), bufsize - strlen(buf), ".%02d MB", (value % (1024 * 1024)) * 100 / (1024 * 1024));
	}
	else if(value > 1024)
	{							// kilos
		Com_sprintf(buf, bufsize, "%d KB", value / 1024);
	}
	else
	{							// bytes
		Com_sprintf(buf, bufsize, "%d bytes", value);
	}
}

// Assumes time is in msec
static void UI_PrintTime(char *buf, int bufsize, int time)
{
	time /= 1000;				// change to seconds

	if(time > 3600)
	{							// in the hours range
		Com_sprintf(buf, bufsize, "%d hr %d min", time / 3600, (time % 3600) / 60);
	}
	else if(time > 60)
	{							// mins
		Com_sprintf(buf, bufsize, "%d min %d sec", time / 60, time % 60);
	}
	else
	{							// secs
		Com_sprintf(buf, bufsize, "%d sec", time);
	}
}

void Text_PaintCenter(float x, float y, float scale, vec4_t color, const char *text, float adjust)
{
	int             len = Text_Width(text, scale, 0);

	Text_Paint(x - len / 2, y, scale, color, text, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
}

void Text_PaintCenter_AutoWrapped(float x, float y, float xmax, float ystep, float scale, vec4_t color, const char *str,
								  float adjust)
{
	int             width;
	char           *s1, *s2, *s3;
	char            c_bcp;
	char            buf[1024];

	if(!str || str[0] == '\0')
		return;

	Q_strncpyz(buf, str, sizeof(buf));
	s1 = s2 = s3 = buf;

	while(1)
	{
		do
		{
			s3++;
		} while(*s3 != ' ' && *s3 != '\0');
		c_bcp = *s3;
		*s3 = '\0';
		width = Text_Width(s1, scale, 0);
		*s3 = c_bcp;
		if(width > xmax)
		{
			if(s1 == s2)
			{
				// fuck, don't have a clean cut, we'll overflow
				s2 = s3;
			}
			*s2 = '\0';
			Text_PaintCenter(x, y, scale, color, s1, adjust);
			y += ystep;
			if(c_bcp == '\0')
			{
				// that was the last word
				// we could start a new loop, but that wouldn't be much use
				// even if the word is too long, we would overflow it (see above)
				// so just print it now if needed
				s2++;
				if(*s2 != '\0')	// if we are printing an overflowing line we have s2 == s3
					Text_PaintCenter(x, y, scale, color, s2, adjust);
				break;
			}
			s2++;
			s1 = s2;
			s3 = s2;
		}
		else
		{
			s2 = s3;
			if(c_bcp == '\0')	// we reached the end
			{
				Text_PaintCenter(x, y, scale, color, s1, adjust);
				break;
			}
		}
	}
}


static void UI_DisplayDownloadInfo(const char *downloadName, float centerPoint, float yStart, float scale)
{
	static char     dlText[] = "Downloading:";
	static char     etaText[] = "Estimated time left:";
	static char     xferText[] = "Transfer rate:";

	int             downloadSize, downloadCount, downloadTime;
	char            dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64];
	int             xferRate;
	int             leftWidth;
	const char     *s;

	downloadSize = trap_Cvar_VariableValue("cl_downloadSize");
	downloadCount = trap_Cvar_VariableValue("cl_downloadCount");
	downloadTime = trap_Cvar_VariableValue("cl_downloadTime");

	leftWidth = 320;

	UI_SetColor(colorWhite);
	Text_PaintCenter(centerPoint, yStart + 112, scale, colorWhite, dlText, 0);
	Text_PaintCenter(centerPoint, yStart + 192, scale, colorWhite, etaText, 0);
	Text_PaintCenter(centerPoint, yStart + 248, scale, colorWhite, xferText, 0);

	if(downloadSize > 0)
	{
		s = va("%s (%d%%)", downloadName, downloadCount * 100 / downloadSize);
	}
	else
	{
		s = downloadName;
	}

	Text_PaintCenter(centerPoint, yStart + 136, scale, colorWhite, s, 0);

	UI_ReadableSize(dlSizeBuf, sizeof dlSizeBuf, downloadCount);
	UI_ReadableSize(totalSizeBuf, sizeof totalSizeBuf, downloadSize);

	if(downloadCount < 4096 || !downloadTime)
	{
		Text_PaintCenter(leftWidth, yStart + 216, scale, colorWhite, "estimating", 0);
		Text_PaintCenter(leftWidth, yStart + 160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0);
	}
	else
	{
		if((uiInfo.uiDC.realTime - downloadTime) / 1000)
		{
			xferRate = downloadCount / ((uiInfo.uiDC.realTime - downloadTime) / 1000);
		}
		else
		{
			xferRate = 0;
		}
		UI_ReadableSize(xferRateBuf, sizeof xferRateBuf, xferRate);

		// Extrapolate estimated completion time
		if(downloadSize && xferRate)
		{
			int             n = downloadSize / xferRate;	// estimated time for entire d/l in secs

			// We do it in K (/1024) because we'd overflow around 4MB
			UI_PrintTime(dlTimeBuf, sizeof dlTimeBuf, (n - (((downloadCount / 1024) * n) / (downloadSize / 1024))) * 1000);

			Text_PaintCenter(leftWidth, yStart + 216, scale, colorWhite, dlTimeBuf, 0);
			Text_PaintCenter(leftWidth, yStart + 160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0);
		}
		else
		{
			Text_PaintCenter(leftWidth, yStart + 216, scale, colorWhite, "estimating", 0);
			if(downloadSize)
			{
				Text_PaintCenter(leftWidth, yStart + 160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0);
			}
			else
			{
				Text_PaintCenter(leftWidth, yStart + 160, scale, colorWhite, va("(%s copied)", dlSizeBuf), 0);
			}
		}

		if(xferRate)
		{
			Text_PaintCenter(leftWidth, yStart + 272, scale, colorWhite, va("%s/Sec", xferRateBuf), 0);
		}
	}
}

/*
========================
UI_DrawConnectScreen

This will also be overlaid on the cgame info screen during loading
to prevent it from blinking away too rapidly on local or lan games.
========================
*/
void UI_DrawConnectScreen(qboolean overlay)
{
	char           *s;
	uiClientState_t cstate;
	char            info[MAX_INFO_VALUE];
	char            text[256];
	float           centerPoint, yStart, scale;

	menuDef_t      *menu = Menus_FindByName("Connect");


	if(!overlay && menu)
	{
		Menu_Paint(menu, qtrue);
	}

	if(!overlay)
	{
		centerPoint = 320;
		yStart = 130;
		scale = 0.3f;
	}
	else
	{
		centerPoint = 320;
		yStart = 32;
		scale = 0.4f;
		return;
	}

	// see what information we should display
	trap_GetClientState(&cstate);

	info[0] = '\0';
	if(trap_GetConfigString(CS_SERVERINFO, info, sizeof(info)))
	{
		Text_PaintCenter(centerPoint, yStart, scale, colorWhite, va("Loading %s", Info_ValueForKey(info, "mapname")), 0);
	}

	if(!Q_stricmp(cstate.servername, "localhost"))
	{
		Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite, va("Starting up..."), ITEM_TEXTSTYLE_SHADOWEDMORE);
	}
	else
	{
		strcpy(text, va("Connecting to %s", cstate.servername));
		Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite, text, ITEM_TEXTSTYLE_SHADOWEDMORE);
	}


	// display global MOTD at bottom
	Text_PaintCenter(centerPoint, 600, scale, colorWhite, Info_ValueForKey(cstate.updateInfoString, "motd"), 0);
	// print any server info (server full, bad version, etc)
	if(cstate.connState < CA_CONNECTED)
	{
		Text_PaintCenter_AutoWrapped(centerPoint, yStart + 176, 630, 20, scale, colorWhite, cstate.messageString, 0);
	}

	if(lastConnState > cstate.connState)
	{
		lastLoadingText[0] = '\0';
	}
	lastConnState = cstate.connState;

	switch (cstate.connState)
	{
		case CA_CONNECTING:
			s = va("Awaiting connection...%i", cstate.connectPacketCount);
			break;
		case CA_CHALLENGING:
			s = va("Awaiting challenge...%i", cstate.connectPacketCount);
			break;
		case CA_CONNECTED:
		{
			char            downloadName[MAX_INFO_VALUE];

			trap_Cvar_VariableStringBuffer("cl_downloadName", downloadName, sizeof(downloadName));
			if(*downloadName)
			{
				UI_DisplayDownloadInfo(downloadName, centerPoint, yStart, scale);
				return;
			}
		}
			s = "Awaiting gamestate...";
			break;
		case CA_LOADING:
			return;
		case CA_PRIMED:
			return;
		default:
			return;
	}


	if(Q_stricmp(cstate.servername, "localhost"))
	{
		Text_PaintCenter(centerPoint, yStart + 80, scale, colorWhite, s, 0);
	}

	// password required / connection rejected information goes here
}


/*
================
cvars
================
*/

typedef struct
{
	vmCvar_t       *vmCvar;
	char           *cvarName;
	char           *defaultString;
	int             cvarFlags;
} cvarTable_t;

vmCvar_t        ui_ffa_fraglimit;
vmCvar_t        ui_ffa_timelimit;

vmCvar_t        ui_tourney_fraglimit;
vmCvar_t        ui_tourney_timelimit;

vmCvar_t        ui_team_fraglimit;
vmCvar_t        ui_team_timelimit;
vmCvar_t        ui_team_friendly;

vmCvar_t        ui_ctf_capturelimit;
vmCvar_t        ui_ctf_timelimit;
vmCvar_t        ui_ctf_friendly;

vmCvar_t        ui_arenasFile;
vmCvar_t        ui_botsFile;
vmCvar_t        ui_spScores1;
vmCvar_t        ui_spScores2;
vmCvar_t        ui_spScores3;
vmCvar_t        ui_spScores4;
vmCvar_t        ui_spScores5;
vmCvar_t        ui_spAwards;
vmCvar_t        ui_spVideos;
vmCvar_t        ui_spSkill;

vmCvar_t        ui_spSelection;

vmCvar_t        ui_browserMaster;
vmCvar_t        ui_browserGameType;
vmCvar_t        ui_browserSortKey;
vmCvar_t        ui_browserShowFull;
vmCvar_t        ui_browserShowEmpty;

vmCvar_t        ui_brassTime;
vmCvar_t        ui_drawCrosshair;
vmCvar_t        ui_drawCrosshairNames;
vmCvar_t        ui_marks;

vmCvar_t        ui_server1;
vmCvar_t        ui_server2;
vmCvar_t        ui_server3;
vmCvar_t        ui_server4;
vmCvar_t        ui_server5;
vmCvar_t        ui_server6;
vmCvar_t        ui_server7;
vmCvar_t        ui_server8;
vmCvar_t        ui_server9;
vmCvar_t        ui_server10;
vmCvar_t        ui_server11;
vmCvar_t        ui_server12;
vmCvar_t        ui_server13;
vmCvar_t        ui_server14;
vmCvar_t        ui_server15;
vmCvar_t        ui_server16;

vmCvar_t        ui_redteam;
vmCvar_t        ui_redteam1;
vmCvar_t        ui_redteam2;
vmCvar_t        ui_redteam3;
vmCvar_t        ui_redteam4;
vmCvar_t        ui_redteam5;
vmCvar_t        ui_blueteam;
vmCvar_t        ui_blueteam1;
vmCvar_t        ui_blueteam2;
vmCvar_t        ui_blueteam3;
vmCvar_t        ui_blueteam4;
vmCvar_t        ui_blueteam5;
vmCvar_t        ui_teamName;
vmCvar_t        ui_dedicated;
vmCvar_t        ui_gameType;
vmCvar_t        ui_netGameType;
vmCvar_t        ui_actualNetGameType;
vmCvar_t        ui_joinGameType;
vmCvar_t        ui_netSource;
vmCvar_t        ui_serverFilterType;
vmCvar_t        ui_opponentName;
vmCvar_t        ui_menuFiles;
vmCvar_t        ui_currentTier;
vmCvar_t        ui_currentMap;
vmCvar_t        ui_currentNetMap;
vmCvar_t        ui_mapIndex;
vmCvar_t        ui_currentOpponent;
vmCvar_t        ui_selectedPlayer;
vmCvar_t        ui_selectedPlayerName;
vmCvar_t        ui_lastServerRefresh_0;
vmCvar_t        ui_lastServerRefresh_1;
vmCvar_t        ui_lastServerRefresh_2;
vmCvar_t        ui_lastServerRefresh_3;
vmCvar_t        ui_singlePlayerActive;
vmCvar_t        ui_scoreAccuracy;
vmCvar_t        ui_scoreImpressives;
vmCvar_t        ui_scoreExcellents;
vmCvar_t        ui_scoreCaptures;
vmCvar_t        ui_scoreDefends;
vmCvar_t        ui_scoreAssists;
vmCvar_t        ui_scoreGauntlets;
vmCvar_t        ui_scoreScore;
vmCvar_t        ui_scorePerfect;
vmCvar_t        ui_scoreTeam;
vmCvar_t        ui_scoreBase;
vmCvar_t        ui_scoreTimeBonus;
vmCvar_t        ui_scoreSkillBonus;
vmCvar_t        ui_scoreShutoutBonus;
vmCvar_t        ui_scoreTime;
vmCvar_t        ui_captureLimit;
vmCvar_t        ui_fragLimit;
vmCvar_t        ui_smallFont;
vmCvar_t        ui_bigFont;
vmCvar_t        ui_findPlayer;
vmCvar_t        ui_Q3Model;
vmCvar_t        ui_hudFiles;
vmCvar_t        ui_recordSPDemo;
vmCvar_t        ui_realCaptureLimit;
vmCvar_t        ui_realWarmUp;
vmCvar_t        ui_serverStatusTimeOut;

//TA: bank values
vmCvar_t        ui_bank;


// bk001129 - made static to avoid aliasing
static cvarTable_t cvarTable[] = {
	{&ui_ffa_fraglimit, "ui_ffa_fraglimit", "20", CVAR_ARCHIVE},
	{&ui_ffa_timelimit, "ui_ffa_timelimit", "0", CVAR_ARCHIVE},

	{&ui_tourney_fraglimit, "ui_tourney_fraglimit", "0", CVAR_ARCHIVE},
	{&ui_tourney_timelimit, "ui_tourney_timelimit", "15", CVAR_ARCHIVE},

	{&ui_team_fraglimit, "ui_team_fraglimit", "0", CVAR_ARCHIVE},
	{&ui_team_timelimit, "ui_team_timelimit", "20", CVAR_ARCHIVE},
	{&ui_team_friendly, "ui_team_friendly", "1", CVAR_ARCHIVE},

	{&ui_ctf_capturelimit, "ui_ctf_capturelimit", "8", CVAR_ARCHIVE},
	{&ui_ctf_timelimit, "ui_ctf_timelimit", "30", CVAR_ARCHIVE},
	{&ui_ctf_friendly, "ui_ctf_friendly", "0", CVAR_ARCHIVE},

	{&ui_arenasFile, "g_arenasFile", "", CVAR_INIT | CVAR_ROM},
	{&ui_botsFile, "g_botsFile", "", CVAR_INIT | CVAR_ROM},
	{&ui_spScores1, "g_spScores1", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spScores2, "g_spScores2", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spScores3, "g_spScores3", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spScores4, "g_spScores4", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spScores5, "g_spScores5", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spAwards, "g_spAwards", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spVideos, "g_spVideos", "", CVAR_ARCHIVE | CVAR_ROM},
	{&ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE},

	{&ui_spSelection, "ui_spSelection", "", CVAR_ROM},

	{&ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE},
	{&ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE},
	{&ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE},
	{&ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE},
	{&ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE},

	{&ui_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE},
	{&ui_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE},
	{&ui_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE},
	{&ui_marks, "cg_marks", "1", CVAR_ARCHIVE},

	{&ui_server1, "server1", "", CVAR_ARCHIVE},
	{&ui_server2, "server2", "", CVAR_ARCHIVE},
	{&ui_server3, "server3", "", CVAR_ARCHIVE},
	{&ui_server4, "server4", "", CVAR_ARCHIVE},
	{&ui_server5, "server5", "", CVAR_ARCHIVE},
	{&ui_server6, "server6", "", CVAR_ARCHIVE},
	{&ui_server7, "server7", "", CVAR_ARCHIVE},
	{&ui_server8, "server8", "", CVAR_ARCHIVE},
	{&ui_server9, "server9", "", CVAR_ARCHIVE},
	{&ui_server10, "server10", "", CVAR_ARCHIVE},
	{&ui_server11, "server11", "", CVAR_ARCHIVE},
	{&ui_server12, "server12", "", CVAR_ARCHIVE},
	{&ui_server13, "server13", "", CVAR_ARCHIVE},
	{&ui_server14, "server14", "", CVAR_ARCHIVE},
	{&ui_server15, "server15", "", CVAR_ARCHIVE},
	{&ui_server16, "server16", "", CVAR_ARCHIVE},
	{&ui_new, "ui_new", "0", CVAR_TEMP},
	{&ui_debug, "ui_debug", "0", CVAR_TEMP},
	{&ui_initialized, "ui_initialized", "0", CVAR_TEMP},
	{&ui_teamName, "ui_teamName", "Pagans", CVAR_ARCHIVE},
	{&ui_opponentName, "ui_opponentName", "Stroggs", CVAR_ARCHIVE},
	{&ui_redteam, "ui_redteam", "Pagans", CVAR_ARCHIVE},
	{&ui_blueteam, "ui_blueteam", "Stroggs", CVAR_ARCHIVE},
	{&ui_dedicated, "ui_dedicated", "0", CVAR_ARCHIVE},
	{&ui_gameType, "ui_gametype", "3", CVAR_ARCHIVE},
	{&ui_joinGameType, "ui_joinGametype", "0", CVAR_ARCHIVE},
	{&ui_netGameType, "ui_netGametype", "3", CVAR_ARCHIVE},
	{&ui_actualNetGameType, "ui_actualNetGametype", "3", CVAR_ARCHIVE},
	{&ui_redteam1, "ui_redteam1", "0", CVAR_ARCHIVE},
	{&ui_redteam2, "ui_redteam2", "0", CVAR_ARCHIVE},
	{&ui_redteam3, "ui_redteam3", "0", CVAR_ARCHIVE},
	{&ui_redteam4, "ui_redteam4", "0", CVAR_ARCHIVE},
	{&ui_redteam5, "ui_redteam5", "0", CVAR_ARCHIVE},
	{&ui_blueteam1, "ui_blueteam1", "0", CVAR_ARCHIVE},
	{&ui_blueteam2, "ui_blueteam2", "0", CVAR_ARCHIVE},
	{&ui_blueteam3, "ui_blueteam3", "0", CVAR_ARCHIVE},
	{&ui_blueteam4, "ui_blueteam4", "0", CVAR_ARCHIVE},
	{&ui_blueteam5, "ui_blueteam5", "0", CVAR_ARCHIVE},
	{&ui_netSource, "ui_netSource", "0", CVAR_ARCHIVE},
	{&ui_menuFiles, "ui_menuFiles", "ui/menus.txt", CVAR_ARCHIVE},
	{&ui_currentTier, "ui_currentTier", "0", CVAR_ARCHIVE},
	{&ui_currentMap, "ui_currentMap", "0", CVAR_ARCHIVE},
	{&ui_currentNetMap, "ui_currentNetMap", "0", CVAR_ARCHIVE},
	{&ui_mapIndex, "ui_mapIndex", "0", CVAR_ARCHIVE},
	{&ui_currentOpponent, "ui_currentOpponent", "0", CVAR_ARCHIVE},
	{&ui_selectedPlayer, "cg_selectedPlayer", "0", CVAR_ARCHIVE},
	{&ui_selectedPlayerName, "cg_selectedPlayerName", "", CVAR_ARCHIVE},
	{&ui_lastServerRefresh_0, "ui_lastServerRefresh_0", "", CVAR_ARCHIVE},
	{&ui_lastServerRefresh_1, "ui_lastServerRefresh_1", "", CVAR_ARCHIVE},
	{&ui_lastServerRefresh_2, "ui_lastServerRefresh_2", "", CVAR_ARCHIVE},
	{&ui_lastServerRefresh_3, "ui_lastServerRefresh_3", "", CVAR_ARCHIVE},
	{&ui_singlePlayerActive, "ui_singlePlayerActive", "0", 0},
	{&ui_scoreAccuracy, "ui_scoreAccuracy", "0", CVAR_ARCHIVE},
	{&ui_scoreImpressives, "ui_scoreImpressives", "0", CVAR_ARCHIVE},
	{&ui_scoreExcellents, "ui_scoreExcellents", "0", CVAR_ARCHIVE},
	{&ui_scoreCaptures, "ui_scoreCaptures", "0", CVAR_ARCHIVE},
	{&ui_scoreDefends, "ui_scoreDefends", "0", CVAR_ARCHIVE},
	{&ui_scoreAssists, "ui_scoreAssists", "0", CVAR_ARCHIVE},
	{&ui_scoreGauntlets, "ui_scoreGauntlets", "0", CVAR_ARCHIVE},
	{&ui_scoreScore, "ui_scoreScore", "0", CVAR_ARCHIVE},
	{&ui_scorePerfect, "ui_scorePerfect", "0", CVAR_ARCHIVE},
	{&ui_scoreTeam, "ui_scoreTeam", "0 to 0", CVAR_ARCHIVE},
	{&ui_scoreBase, "ui_scoreBase", "0", CVAR_ARCHIVE},
	{&ui_scoreTime, "ui_scoreTime", "00:00", CVAR_ARCHIVE},
	{&ui_scoreTimeBonus, "ui_scoreTimeBonus", "0", CVAR_ARCHIVE},
	{&ui_scoreSkillBonus, "ui_scoreSkillBonus", "0", CVAR_ARCHIVE},
	{&ui_scoreShutoutBonus, "ui_scoreShutoutBonus", "0", CVAR_ARCHIVE},
	{&ui_fragLimit, "ui_fragLimit", "10", 0},
	{&ui_captureLimit, "ui_captureLimit", "5", 0},
	{&ui_smallFont, "ui_smallFont", "0.2", CVAR_ARCHIVE},
	{&ui_bigFont, "ui_bigFont", "0.5", CVAR_ARCHIVE},
	{&ui_findPlayer, "ui_findPlayer", "Sarge", CVAR_ARCHIVE},
	{&ui_Q3Model, "ui_q3model", "0", CVAR_ARCHIVE},
	{&ui_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE},
	{&ui_recordSPDemo, "ui_recordSPDemo", "0", CVAR_ARCHIVE},
	{&ui_teamArenaFirstRun, "ui_teamArenaFirstRun", "0", CVAR_ARCHIVE},
	{&ui_realWarmUp, "g_warmup", "20", CVAR_ARCHIVE},
	{&ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART},
	{&ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE},

	{&ui_bank, "ui_bank", "0", 0},

};

// bk001129 - made static to avoid aliasing
static int      cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]);


/*
=================
UI_RegisterCvars
=================
*/
void UI_RegisterCvars(void)
{
	int             i;
	cvarTable_t    *cv;

	for(i = 0, cv = cvarTable; i < cvarTableSize; i++, cv++)
	{
		trap_Cvar_Register(cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags);
	}
}

/*
=================
UI_UpdateCvars
=================
*/
void UI_UpdateCvars(void)
{
	int             i;
	cvarTable_t    *cv;

	for(i = 0, cv = cvarTable; i < cvarTableSize; i++, cv++)
	{
		trap_Cvar_Update(cv->vmCvar);
	}
}


/*
=================
ArenaServers_StopRefresh
=================
*/
static void UI_StopServerRefresh(void)
{
	int             count;

	if(!uiInfo.serverStatus.refreshActive)
	{
		// not currently refreshing
		return;
	}
	uiInfo.serverStatus.refreshActive = qfalse;
	Com_Printf("%d servers listed in browser with %d players.\n",
			   uiInfo.serverStatus.numDisplayServers, uiInfo.serverStatus.numPlayersOnServers);
	count = trap_LAN_GetServerCount(ui_netSource.integer);
	if(count - uiInfo.serverStatus.numDisplayServers > 0)
	{
		Com_Printf("%d servers not listed due to packet loss or pings higher than %d\n",
				   count - uiInfo.serverStatus.numDisplayServers, (int)trap_Cvar_VariableValue("cl_maxPing"));
	}

}

/*
=================
UI_DoServerRefresh
=================
*/
static void UI_DoServerRefresh(void)
{
	qboolean        wait = qfalse;

	if(!uiInfo.serverStatus.refreshActive)
	{
		return;
	}
	if(ui_netSource.integer != AS_FAVORITES)
	{
		if(ui_netSource.integer == AS_LOCAL)
		{
			if(!trap_LAN_GetServerCount(ui_netSource.integer))
			{
				wait = qtrue;
			}
		}
		else
		{
			if(trap_LAN_GetServerCount(ui_netSource.integer) < 0)
			{
				wait = qtrue;
			}
		}
	}

	if(uiInfo.uiDC.realTime < uiInfo.serverStatus.refreshtime)
	{
		if(wait)
		{
			return;
		}
	}

	// if still trying to retrieve pings
	if(trap_LAN_UpdateVisiblePings(ui_netSource.integer))
	{
		uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
	}
	else if(!wait)
	{
		// get the last servers in the list
		UI_BuildServerDisplayList(2);
		// stop the refresh
		UI_StopServerRefresh();
	}
	//
	UI_BuildServerDisplayList(qfalse);
}

/*
=================
UI_StartServerRefresh
=================
*/
static void UI_StartServerRefresh(qboolean full)
{
	int             i;
	char           *ptr;

	qtime_t         q;

	trap_RealTime(&q);
	trap_Cvar_Set(va("ui_lastServerRefresh_%i", ui_netSource.integer),
				  va("%s-%i, %i at %i:%02i", MonthAbbrev[q.tm_mon], q.tm_mday, 1900 + q.tm_year, q.tm_hour, q.tm_min));

	if(!full)
	{
		UI_UpdatePendingPings();
		return;
	}

	uiInfo.serverStatus.refreshActive = qtrue;
	uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 1000;
	// clear number of displayed servers
	uiInfo.serverStatus.numDisplayServers = 0;
	uiInfo.serverStatus.numPlayersOnServers = 0;
	// mark all servers as visible so we store ping updates for them
	trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue);
	// reset all the pings
	trap_LAN_ResetPings(ui_netSource.integer);
	//
	if(ui_netSource.integer == AS_LOCAL)
	{
		trap_Cmd_ExecuteText(EXEC_NOW, "localservers\n");
		uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
		return;
	}

	uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000;
	if(ui_netSource.integer == AS_GLOBAL || ui_netSource.integer == AS_MPLAYER)
	{
		if(ui_netSource.integer == AS_GLOBAL)
		{
			i = 0;
		}
		else
		{
			i = 1;
		}

		ptr = UI_Cvar_VariableString("debug_protocol");
		if(strlen(ptr))
		{
			trap_Cmd_ExecuteText(EXEC_NOW, va("globalservers %d %s full empty\n", i, ptr));
		}
		else
		{
			trap_Cmd_ExecuteText(EXEC_NOW, va("globalservers %d %d full empty\n", i, (int)trap_Cvar_VariableValue("protocol")));
		}
	}
}
